<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="description" content="简洁而强大的ChatGPT应用">
    <meta name="theme-color" content="#edeff2">
    <meta name="apple-mobile-web-app-status-bar-style" content="#edeff2">
    <meta name="msapplication-TileColor" content="#edeff2">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="msapplication-TileImage" content="icon.png">
    <link rel="manifest" crossorigin="use-credentials" href="manifest.json">
    <link rel="icon" type="image/png" href="icon.png">
    <link rel="apple-touch-icon" href="icon.png" sizes="512x512">
    <title>ChatGPT</title>
    <style>
        .requestBody,
        .response .responseBody {
            max-width: calc(100% - 84px);
        }

        .bottom_wrapper {
            max-width: 100%;
        }

        * {
            box-sizing: border-box;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
        }

        html {
            width: 100%;
            height: 100%;
        }

        :root {
            --background: #edeff2;
            --chat-back: #fff;
            --main-back: #f6f6f6;
            --active-btn: #e0e0e0;
            --lighter-active: #eaeaea;
            --sel-btn: #d0d0d0;
            --btn-color: #404040;
            --text-color: #909090;
            --chat-text-color: #24292f;
            --response-color: #f7f7f8;
            --lighter-active-color: #e8e8e8;
            --lighter-text-color: #555;
            --svg-color: #808080;
            --lighter-svg-color: #c0c0c0;
            --code-color: #f0f0f0;
            --black-color: #000;
            --gpt-icon-color: #0d0d0d;
        }

        [data-theme="dark"] {
            --background: #1f1f1f;
            --chat-back: #3c3c3c;
            --main-back: #333;
            --active-btn: #1f1f1f;
            --lighter-active: #151515;
            --sel-btn: #1e1e1e;
            --btn-color: #bfbfbf;
            --text-color: #8f8f8f;
            --chat-text-color: #c9d1d9;
            --response-color: #2f2f2f;
            --lighter-active-color: #171717;
            --lighter-text-color: #aaa;
            --svg-color: #7f7f7f;
            --lighter-svg-color: #3f3f3f;
            --code-color: #101010;
            --black-color: #fff;
            --gpt-icon-color: #ececec;
        }

        body {
            background-color: var(--background);
            width: 100%;
            height: 100%;
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .chat_window {
            position: absolute;
            width: 100%;
            max-width: 1188px;
            height: 100%;
            max-height: 888px;
            border-radius: 8px;
            background-color: var(--chat-back);
            overflow: hidden;
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
        }

        .chat_window>.overlay {
            width: 100%;
            height: 100%;
            position: absolute;
            left: 0;
            top: 0;
            background-color: rgba(0, 0, 0, .3);
            z-index: 90;
            cursor: pointer;
            visibility: hidden;
            opacity: 0;
            -webkit-tap-highlight-color: transparent;
            transition: all 250ms;
            transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
        }

        @media screen and (max-width: 1188px) and (max-height: 888px) {
            #toggleFull {
                display: none;
            }
        }

        @media screen and (min-width: 800px) {
            .chat_window {
                display: flex;
            }

            .mainContent {
                width: calc(100% - 250px);
            }

            .chat_window>.nav {
                position: relative;
                margin-left: -250px;
                transition: margin-left 250ms;
                transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
            }

            .show-nav .nav {
                margin-left: 0;
            }

            #sysDialog {
                max-width: 600px;
            }

            .sysContent {
                display: flex;
            }

            .sysSwitch {
                flex-shrink: 0;
                width: 160px;
            }

            .sysSwitch>div {
                padding-left: 6px;
            }

            .sysDetail {
                margin-left: 12px;
                margin-top: 5px;
            }
        }

        @media screen and (max-width: 800px) {
            .chat_window {
                display: block;
            }

            .mainContent {
                width: 100%;
            }

            .chat_window>.nav {
                position: absolute !important;
                left: -250px;
                transition: left 250ms;
                transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
            }

            .show-nav .nav {
                left: 0;
                box-shadow: rgba(0, 0, 0, 0.2) 0px 8px 10px -5px, rgba(0, 0, 0, 0.14) 0px 16px 24px 2px, rgba(0, 0, 0, 0.12) 0px 6px 30px 5px;
            }

            .show-nav .overlay {
                visibility: visible;
                opacity: 1;
            }

            #sysDialog {
                max-width: 400px;
            }

            .sysSwitch {
                display: flex;
            }

            .sysSwitch>div {
                width: 50%;
                justify-content: center;
            }

            .sysDetail {
                margin-top: 8px;
            }
        }

        .full_window {
            max-width: none;
            max-height: none;
        }

        .chat_window>.nav {
            width: 250px;
            height: 100%;
            border-right: 1px solid var(--active-btn);
            background-color: var(--main-back);
            top: 0;
            z-index: 99;
            flex-shrink: 0;
            display: flex;
            flex-direction: column;
        }

        .mainContent {
            height: 100%;
            position: relative;
            display: flex;
            flex-direction: column;
            flex: 1;
        }

        .top_menu {
            background-color: var(--main-back);
            width: 100%;
            height: 46px;
            padding: 3px 0;
        }

        .top_menu .toggler {
            margin-left: 8px;
            width: 40px;
            height: 40px;
            float: left;
            padding: 5px 7px;
            border-radius: 4px;
            cursor: pointer;
            -webkit-tap-highlight-color: transparent;
        }

        .top_menu .toggler:hover {
            background: var(--active-btn);
        }

        .top_menu .toggler .button {
            width: 26px;
            height: 4px;
            border-radius: 4px;
            position: absolute;
            pointer-events: none;
        }

        .top_menu .toggler .button.close {
            margin-top: 3px;
            background-color: #99c959;
        }

        .top_menu .toggler .button.minimize {
            margin-top: 13px;
            background-color: #f8b26a;
        }

        .top_menu .toggler .button.maximize {
            margin-top: 23px;
            background-color: #e15b64;
        }

        .top_menu .title {
            color: var(--text-color);
            height: 40px;
            position: relative;
            pointer-events: none;
        }

        #selector {
            position: absolute;
            top: 50%;
            left: 50%;
            padding: 0 4px 0 10px;
            cursor: pointer;
            font-size: 20px;
            transform: translate(-50%, -50%);
            pointer-events: auto;
            border-radius: 4px;
            -webkit-user-select: none;
            user-select: none;
            display: flex;
            align-items: center;
        }

        #selector:hover {
            background-color: var(--active-btn);
        }

        #modelVer {
            margin-left: 6px;
            margin-right: 4px;
            height: 40px;
            line-height: 38px;
        }

        .messages {
            position: relative;
            flex: 1;
            overflow-x: hidden;
            overflow-y: auto;
            font-size: 16px;
            color: var(--chat-text-color);
            text-align: center;
        }

        .messages::-webkit-scrollbar,
        #chatlog .responseBody pre>code::-webkit-scrollbar,
        #setDialog::-webkit-scrollbar,
        .allList::-webkit-scrollbar,
        .sysDetail::-webkit-scrollbar,
        #apiSelect::-webkit-scrollbar {
            width: 10px;
            height: 10px;
        }

        .messages::-webkit-scrollbar-track,
        #chatlog .responseBody pre>code::-webkit-scrollbar-track,
        #setDialog::-webkit-scrollbar-track,
        .allList::-webkit-scrollbar-track,
        .sysDetail::-webkit-scrollbar-track,
        #apiSelect::-webkit-scrollbar-track {
            background-clip: padding-box;
            background: transparent;
            border: none;
        }

        .messages::-webkit-scrollbar-corner,
        #chatlog .responseBody pre>code::-webkit-scrollbar-corner,
        #setDialog::-webkit-scrollbar-corner,
        .allList::-webkit-scrollbar-corner,
        .sysDetail::-webkit-scrollbar-corner,
        #apiSelect::-webkit-scrollbar-corner {
            background-color: transparent;
        }

        .messages::-webkit-scrollbar-thumb,
        #chatlog .responseBody pre>code::-webkit-scrollbar-thumb,
        #setDialog::-webkit-scrollbar-thumb,
        .allList::-webkit-scrollbar-thumb,
        .sysDetail::-webkit-scrollbar-thumb,
        #apiSelect::-webkit-scrollbar-thumb {
            background-color: rgba(0, 0, 0, 0.1);
            background-clip: padding-box;
            border: solid transparent;
            border-radius: 10px;
        }

        .messages::-webkit-scrollbar-thumb:hover,
        #chatlog .responseBody pre>code::-webkit-scrollbar-thumb:hover,
        #setDialog::-webkit-scrollbar-thumb:hover,
        .allList::-webkit-scrollbar-thumb:hover,
        .sysDetail::-webkit-scrollbar-thumb:hover,
        #apiSelect::-webkit-scrollbar-thumb:hover {
            background-color: rgba(0, 0, 0, 0.4);
        }

        #chatlog {
            word-wrap: break-word;
            text-align: start;
        }

        .chatAvatar {
            margin: 14px 12px 13px 12px;
            width: 30px;
            height: 30px;
            flex-shrink: 0;
            border-radius: 2px;
        }

        .response>.chatAvatar {
            margin-top: 15px;
            margin-bottom: 15px;
        }

        .gptAvatar>svg {
            margin-top: 3px;
            margin-left: 3px;
            fill: var(--gpt-icon-color);
        }

        .chatAvatar>img {
            display: block;
            width: 100%;
            height: 100%;
            border-radius: 2px;
        }

        .modelAvatar>svg,
        .chatAvatar>svg {
            display: block;
        }

        #chatlog .request {
            position: relative;
            display: flex;
            justify-content: center;
        }

        .requestBody {
            white-space: pre-wrap;
            margin: 18px 0;
            flex: 1;
        }

        #chatlog .response {
            background: var(--response-color);
            position: relative;
            display: flex;
            justify-content: center;
        }

        .response .responseBody {
            margin: 18px 0;
            flex: 1;
        }

        .response .markdown-body {
            background: var(--response-color) !important;
        }

        #chatlog .markdown-body a {
            padding: 0 1px 0 2px;
        }

        #chatlog .markdown-body pre {
            padding: 10px 10px 5px 10px;
            position: relative;
            background: var(--code-color);
        }

        #chatlog .markdown-body .mermaid {
            margin-bottom: 8px;
            padding: 0 10px 0 10px;
            position: relative;
            background: var(--code-color);
            border-radius: 6px;
        }

        .mermaid>svg {
            cursor: pointer;
            max-height: 600px;
            display: block;
        }

        #chatlog .markdown-body pre>code {
            overflow-x: auto;
            display: block;
        }

        #chatlog .markdown-body ul {
            list-style-type: disc;
        }

        .m-mdic-copy-wrapper {
            position: absolute;
            top: 5px;
            right: 16px;
            z-index: 1;
            -webkit-user-select: none;
            user-select: none;
        }

        .m-mdic-copy-wrapper span.u-mdic-copy-code_lang {
            position: absolute;
            top: 3px;
            right: calc(100% + 4px);
            font-family: system-ui;
            font-size: 12px;
            line-height: 18px;
            color: #bbb;
        }

        .m-mdic-copy-wrapper div.u-mdic-copy-notify {
            position: absolute;
            top: 0;
            right: 0;
            padding: 3px 6px;
            border: 0;
            border-radius: 3px;
            background: none;
            font-family: system-ui;
            font-size: 12px;
            line-height: 18px;
            color: var(--lighter-text-color);
            outline: none;
            right: 100%;
            padding-right: 4px;
        }

        .m-mdic-copy-wrapper button.u-mdic-copy-btn {
            position: relative;
            top: 0;
            right: 0;
            padding: 3px 6px;
            border: 0;
            border-radius: 3px;
            background: none;
            font-family: system-ui;
            font-size: 12px;
            line-height: 18px;
            color: #bbb;
            outline: none;
            cursor: pointer;
            transition: color 250ms;
        }

        .m-mdic-copy-wrapper span.u-mdic-copy-code_lang::before,
        .m-mdic-copy-wrapper div.u-mdic-copy-notify::before,
        .m-mdic-copy-wrapper button.u-mdic-copy-btn::before {
            content: attr(text);
        }

        .m-mdic-copy-wrapper button.u-mdic-copy-btn:hover {
            color: var(--lighter-text-color);
        }

        #stopChat {
            display: none;
            margin: 0 auto;
            margin-top: 3px;
            width: 80px;
            height: 32px;
            text-align: center;
            line-height: 32px;
            color: white;
            background: #f8b26a;
            cursor: pointer;
            border-radius: 3px;
            position: sticky;
            bottom: 2px;
            justify-content: center;
            align-items: center;
        }

        #stopChat>svg {
            margin-right: 8px;
        }

        #stopChat:hover {
            background: #f0aa60;
        }

        .bottom_wrapper {
            position: relative;
            width: 100%;
            padding: 10px 10px;
            margin: 0 auto;
        }

        .bottom_wrapper .message_input_wrapper {
            border: none;
            width: calc(100% - 139px);
            position: relative;
            text-align: left;
        }

        .bottom_wrapper .message_input_wrapper .message_input_text {
            border-radius: 4px;
            border: none;
            outline: none;
            resize: none;
            background-color: var(--main-back);
            color: var(--chat-text-color);
            height: 47px;
            font-size: 16px;
            max-height: 200px;
            padding: 13px 0 13px 13px;
            width: 100%;
            display: block;
            transition: background-color 250ms;
        }

        .bottom_wrapper .message_input_wrapper .message_input_text:focus {
            background-color: var(--code-color);
        }

        .bottom_wrapper .message_input_wrapper .message_input_text::-webkit-scrollbar {
            display: none;
            width: 0;
            height: 0;
        }

        #sendbutton {
            width: 80px;
            height: 47px;
            font-size: 18px;
            font-weight: bold;
            border-radius: 3px;
            background-color: #b8da8b;
            border: none;
            padding: 0;
            color: #fff;
            cursor: pointer;
            transition: all 250ms;
            text-align: center;
            float: right;
            position: absolute;
            right: 63px;
            bottom: 10px;
            cursor: not-allowed;
        }

        .activeSendBtn {
            background-color: #99c959 !important;
            cursor: pointer !important;
        }

        .activeSendBtn:hover {
            background-color: #90c050 !important;
        }

        .clearConv {
            position: absolute;
            right: 10px;
            bottom: 10px;
            width: 47px;
            height: 47px;
            border-radius: 3px;
            background: var(--text-color);
            border: none;
            color: #fff;
            cursor: pointer;
        }

        .clearConv>svg {
            margin: 0 auto;
        }

        .clearConv:hover {
            background: var(--svg-color);
        }

        .clearConv svg:first-child {
            display: none;
        }

        .clearConv svg:nth-child(2) {
            display: block;
        }

        .closeConv {
            background: var(--active-btn);
        }

        .closeConv:hover {
            background: var(--lighter-active-color);
        }

        .closeConv svg:first-child {
            display: block;
        }

        .closeConv svg:nth-child(2) {
            display: none;
        }

        .loaded>span {
            display: inline-block;
        }

        .loaded>svg {
            display: none;
        }

        .loading {
            background: var(--active-btn) !important;
        }

        .loading>span {
            display: none;
        }

        .loading>svg {
            display: block;
        }

        .switch-slide {
            display: inline-block;
            vertical-align: middle;
        }

        .switch-slide-label {
            display: block;
            width: 38px;
            height: 18px;
            background: var(--text-color);
            border-radius: 30px;
            cursor: pointer;
            position: relative;
            -webkit-transition: all 250ms;
            transition: all 250ms;
        }

        .switch-slide-label:after {
            content: "";
            display: block;
            width: 16px;
            height: 16px;
            border-radius: 100%;
            background: #fff;
            box-shadow: 0 1px 1px rgba(0, 0, 0, .1);
            position: absolute;
            left: 1px;
            top: 1px;
            -webkit-transform: translateZ(0);
            transform: translateZ(0);
            -webkit-transition: all 250ms;
            transition: all 250ms;
        }

        .switch-slide input:checked+label {
            background: #99c959;
            -webkit-transition: all 250ms;
            transition: all 250ms;
        }

        .switch-slide input:checked+label:after {
            left: 21px;
        }

        .settings {
            margin-right: 10px;
            display: flex;
            position: absolute;
            right: 0;
            top: 3px;
        }

        .setBtn {
            margin-left: 2px;
            cursor: pointer;
            padding: 5px;
            border: none;
            background-color: transparent;
            border-radius: 4px;
        }

        .setBtn>svg {
            display: block;
            color: var(--text-color);
        }

        .setBtn:hover {
            background: var(--active-btn);
        }

        #setting {
            right: 15px;
        }

        #toggleFull {
            right: 56px;
        }

        #toggleLight *,
        #toggleFull * {
            pointer-events: none;
        }

        .showSetting {
            background: var(--lighter-svg-color) !important;
        }

        .showModels {
            background: var(--active-btn) !important;
        }

        .showModels>svg:last-child {
            transform: scaleY(-1);
        }

        #modelDialog {
            color: var(--btn-color);
            position: absolute;
            z-index: 2;
            background: var(--main-back);
            width: 240px;
            left: 50%;
            transform: translateX(-50%);
            top: 46px;
            overflow-y: auto;
            max-height: calc(100% - 55px);
            -webkit-user-select: none;
            user-select: none;
            border-radius: 4px;
            padding: 8px 5px 8px 5px;
            box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
        }

        .modelSingle {
            height: 40px;
            border-radius: 3px;
            cursor: pointer;
            line-height: 40px;
            display: flex;
            align-items: center;
            position: relative;
        }

        .modelSingle * {
            pointer-events: none;
        }

        .modelSingle:hover {
            background-color: var(--active-btn);
        }

        .activeModel::before {
            content: "";
            position: absolute;
            right: 15px;
            top: 7px;
            width: 8px;
            height: 16px;
            border-color: #99c959;
            border-style: solid;
            border-width: 0 4px 4px 0;
            transform: rotate(45deg);
        }

        .modelAvatar {
            margin-left: 6px;
            margin-right: 10px;
            width: 30px;
            flex-shrink: 0;
            border-radius: 2px;
            height: 30px;
        }

        #setDialog {
            color: var(--btn-color);
            position: absolute;
            z-index: 2;
            background: var(--main-back);
            width: 320px;
            right: 6px;
            top: 46px;
            overflow-y: auto;
            max-height: calc(100% - 55px);
            -webkit-user-select: none;
            user-select: none;
            border-radius: 5px;
            padding: 8px 12px 8px 12px;
            box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
        }

        #setDialog input {
            width: 100%;
        }

        #setDialog .inlineTitle {
            display: inline-block;
            width: 88px;
            line-height: 16px;
            vertical-align: middle;
        }

        #convOption,
        #speechOption,
        #speechDetail,
        #recOption {
            margin-bottom: 6px;
        }

        #convOption>div,
        #speechOption>div,
        #speechDetail>div,
        #recOption div {
            margin-top: 7px;
        }

        #voiceRecSetting select,
        #speechDetail select {
            background: var(--chat-back);
            color: var(--chat-text-color);
        }

        .inputTextClass {
            outline: none;
            border-radius: 2px;
            margin-top: 3px;
            height: 32px;
            font-size: 15px;
            padding-left: 6px;
            background: var(--chat-back);
            color: var(--chat-text-color);
            border: none;
        }

        .areaTextClass {
            width: 100%;
            height: 80px;
            display: block;
            resize: none;
            padding: 6px;
        }

        input[type="range"] {
            -webkit-appearance: none;
            appearance: none;
            display: block;
            margin: 4px 0 3px 0;
            height: 8px;
            background: var(--text-color);
            border-radius: 5px;
            background-image: linear-gradient(#99c959, #99c959);
            background-size: 100% 100%;
            background-repeat: no-repeat;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 15px;
            width: 15px;
            border-radius: 50%;
            background: #99c959;
            cursor: ew-resize;
        }

        input[type=range]::-webkit-slider-runnable-track {
            -webkit-appearance: none;
            box-shadow: none;
            border: none;
            background: transparent;
        }

        .justSetLine {
            display: flex;
            justify-content: space-between;
        }

        .justSetBtn {
            height: 32px;
            border-radius: 3px;
            line-height: 32px;
            background: var(--lighter-active);
            text-align: center;
            padding: 0px 8px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
        }

        .justSetBtn:hover {
            background-color: var(--sel-btn);
        }

        .justSetBtn>svg {
            margin-right: 3px;
        }

        .readyTestVoice>div:not(:first-child) {
            display: none;
        }

        .pauseTestVoice>div:nth-child(1),
        .pauseTestVoice>div:nth-child(3) {
            display: none;
        }

        .resumeTestVoice>div:nth-child(1),
        .resumeTestVoice>div:nth-child(2) {
            display: none;
        }

        .presetSelect>div {
            display: inline-block;
        }

        .presetSelect select {
            outline: none;
            border-radius: 3px;
            width: 128px;
            border-color: rgba(0, 0, 0, .3);
            background: var(--chat-back);
            color: var(--chat-text-color);
        }

        .selectDef {
            display: flex;
            justify-content: space-between;
            font-size: 13px;
            color: var(--text-color);
        }

        #preSetSpeech {
            width: 100%;
            outline: none;
            height: 30px;
            font-size: 14px;
            margin-top: 5px;
            border-radius: 3px;
            border-color: rgba(0, 0, 0, .3);
        }

        .mdOption {
            flex-shrink: 0;
            position: relative;
            width: 30px;
            pointer-events: none;
        }

        .mdOption>div {
            pointer-events: auto;
            cursor: pointer;
        }

        .mdOption svg * {
            pointer-events: none;
        }

        .refreshReq svg:not(:first-child) {
            display: none;
        }

        .halfRefReq svg:not(:nth-child(2)) {
            display: none;
        }

        .optionItems {
            position: absolute;
            bottom: -12px;
            display: flex;
            justify-content: space-between;
            visibility: hidden;
            z-index: 1;
            color: var(--svg-color);
        }

        .optionItems:hover {
            visibility: visible;
        }

        .request:hover .optionItems,
        .request:hover .voiceCls,
        .response:hover .optionItems,
        .response:hover .voiceCls {
            visibility: visible;
        }

        .optionItem {
            border-radius: 9px;
            height: 24px;
            width: 32px;
            border: 1px solid var(--active-btn);
            background-color: var(--response-color);
            display: flex !important;
            justify-content: center;
            align-items: center;
        }

        .optionItem * {
            pointer-events: none;
        }

        .optionItem:hover {
            background: var(--active-btn);
        }

        .voiceCls {
            position: relative;
            height: 100%;
            visibility: hidden;
            display: flex;
            align-items: center;
        }

        .voiceCls>svg {
            color: var(--lighter-svg-color);
            display: block;
            margin-left: 5px;
            position: relative;
        }

        .voiceCls:hover>svg {
            color: var(--svg-color);
        }

        .showVoiceCls,
        .showVoiceCls .markdown-body {
            background: var(--active-btn) !important;
        }

        .showVoiceCls .voiceCls {
            visibility: visible !important;
        }

        .showEditReq {
            position: sticky !important;
            top: 0;
            bottom: 0;
            z-index: 1;
        }

        .showEditReq,
        .showEditReq .markdown-body {
            background: var(--active-btn) !important;
        }

        .readyVoice svg:not(:first-child) {
            display: none;
        }

        .pauseVoice svg:not(:nth-child(2)) {
            display: none;
        }

        .resumeVoice svg:not(:nth-child(3)) {
            display: none;
        }

        #voiceTypes>span {
            border-radius: 3px;
            margin-left: 4px;
            height: 28px;
            line-height: 26px;
            display: inline-block;
            cursor: pointer;
            padding: 1px 5px;
        }

        #voiceTypes>span:hover {
            background: var(--active-btn);
        }

        .selVoiceType {
            background: var(--sel-btn) !important;
        }

        .navHeader {
            width: 100%;
            padding: 5px 10px;
            display: flex;
            justify-content: space-between;
        }

        #newChat {
            text-align: center;
            width: 80%;
            height: 40px;
            border-radius: 3px;
            background: var(--lighter-active-color);
            color: var(--btn-color);
            display: flex;
            align-items: center;
            justify-content: center;
            -webkit-user-select: none;
            user-select: none;
            cursor: pointer;
            flex: 1;
        }

        .navHeader>div:hover {
            background: var(--active-btn) !important;
        }

        #newChat>svg {
            margin-right: 2px;
        }

        #newFolder {
            height: 40px;
            width: 40px;
            margin-left: 10px;
            border-radius: 3px;
            color: var(--btn-color);
            cursor: pointer;
            position: relative;
            background: var(--lighter-active-color);
            -webkit-user-select: none;
            user-select: none;
        }

        #newFolder>svg {
            display: block;
            margin: 8px auto;
        }

        .extraChat {
            padding: 2px 10px 6px 10px;
            position: relative;
        }

        #searchChat {
            width: 100%;
            height: 36px;
            padding-left: 10px;
            padding-right: 45px;
            font-size: 16px;
            outline: none;
            border: none;
            color: var(--chat-text-color);
            background: var(--lighter-active-color);
            border-radius: 3px;
            -webkit-user-select: none;
            user-select: none;
        }

        #searchChat:focus {
            background: var(--active-btn)
        }

        #searchChat:placeholder-shown+#clearSearch {
            display: none;
        }

        #clearSearch {
            position: absolute;
            right: 34px;
            top: 8px;
            cursor: pointer;
            color: var(--btn-color);
        }

        #clearSearch:hover {
            color: var(--black-color);
        }

        #clearSearch>svg {
            display: block;
        }

        .seledSearch {
            background: var(--lighter-svg-color) !important;
        }

        #matchCaseSearch {
            position: absolute;
            right: 12px;
            top: 8px;
            cursor: pointer;
            border-radius: 3px;
            color: var(--btn-color);
        }

        #matchCaseSearch:hover {
            background: var(--sel-btn);
        }

        #matchCaseSearch>svg {
            display: block;
        }

        .navFooter {
            padding-bottom: 8px;
        }

        .navFooter .divider {
            width: 100%;
            border-top: 1px solid var(--active-btn);
            margin: 4px 0;
        }

        .navFunc {
            padding-top: 5px;
            display: flex;
            justify-content: space-around;
        }

        .navFunc svg {
            display: block;
        }

        .navFunc>div,
        .navFunc>label {
            border-radius: 20px;
            text-align: center;
            padding: 8px 8px;
            color: var(--btn-color);
            font-size: 14px;
            cursor: pointer;
        }

        .navFunc>div:hover,
        .navFunc>label:hover {
            background: var(--active-btn);
        }

        .navFooter .links {
            text-align: center;
            -webkit-user-select: none;
            user-select: none;
        }

        .navFooter .links a {
            color: var(--btn-color);
            text-decoration: none;
        }

        .navFooter .links a:hover {
            color: var(--chat-text-color) !important;
        }

        .navFooter .links a:visited {
            color: var(--btn-color);
        }

        .allList {
            width: 100%;
            position: relative;
            flex: 1;
            overflow-y: auto;
        }

        #chatList {
            min-height: 50px;
        }

        .dragingLi {
            filter: brightness(90%);
        }

        .dragingChat {
            background: var(--lighter-active-color);
        }

        .expandFolder>.headLi>svg {
            transform: rotate(90deg);
        }

        .expandFolder>.chatsInFolder {
            display: block;
        }

        .chatsInFolder {
            display: none;
            margin-left: 22px;
            padding-left: 2px;
            border-left: 1px solid var(--text-color);
        }

        .headLi,
        .chatLi {
            cursor: pointer;
            width: 100%;
            height: 50px;
            color: var(--text-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
            position: relative;
        }

        .headLi *,
        .chatLi * {
            pointer-events: none;
        }

        .headLi>svg,
        .chatLi>svg {
            margin-left: 10px;
            color: var(--btn-color);
            pointer-events: none;
        }

        .folderOption svg,
        .chatOption svg {
            pointer-events: auto;
        }

        .headLi svg *,
        .chatLi svg * {
            pointer-events: none;
        }

        .headLi .folderInfo {
            position: absolute;
            left: 40px;
            height: 40px;
            max-width: calc(100% - 115px);
        }

        .chatLi .chatInfo {
            position: absolute;
            left: 40px;
            height: 40px;
            max-width: calc(100% - 90px);
        }

        .folderInfo *,
        .chatInfo * {
            -webkit-user-select: none;
            user-select: none;
        }

        .chatInfo span {
            background: #f8b26a;
        }

        .headLi .folderName,
        .chatLi .chatName {
            color: var(--btn-color);
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
            line-height: 20px;
            height: 20px;
        }

        .headLi .folderNum,
        .chatLi .chatPre {
            color: var(--btn-color);
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
            font-size: 12px;
            line-height: 20px;
            height: 20px;
        }

        .headLi .folderOption,
        .chatLi .chatOption {
            visibility: hidden;
            display: flex;
            color: #777;
            margin-right: 2px;
        }

        .folderLi .chatLi>svg {
            margin-left: 5px;
        }

        .folderLi .chatLi .chatInfo {
            left: 35px;
            max-width: calc(100% - 85px);
        }

        .folderLi .chatLi #activeChatEdit {
            left: 32px;
            width: calc(100% - 60px)
        }

        .folderLi:hover {
            background: var(--lighter-active);
        }

        .chatLi:hover {
            background: var(--active-btn);
        }

        .headLi:hover .folderOption,
        .chatLi:hover .chatOption {
            visibility: visible !important;
        }

        .activeFolder,
        .activeChatLi {
            background: var(--sel-btn) !important;
        }

        .activeChatLi .chatOption {
            visibility: visible !important;
        }

        .folderOption>svg:hover,
        .chatOption>svg:hover {
            color: #444;
        }

        #activeChatEdit {
            position: absolute;
            left: 37px;
            font-size: 16px;
            border-radius: 2px;
            color: var(--chat-text-color);
            background: var(--chat-back);
            outline: none;
            border: none;
            pointer-events: auto;
            height: 24px;
            line-height: 24px;
            width: calc(100% - 65px);
            padding: 20px 3px;
            z-index: 1;
        }

        #loadMask {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            z-index: 100;
            background-color: var(--background);
        }

        #loadMask>div {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
        }

        @keyframes loading {

            0%,
            100% {
                transform: scale(0);
            }

            50% {
                transform: scale(1);
            }
        }

        .loadingCSSIcon {
            position: relative;
            display: flex;
            align-items: center;
            justify-content: space-evenly;
        }

        .loadingCSSIcon div {
            width: 15%;
            height: 0;
            padding-bottom: 15%;
            border-radius: 50%;
            animation: loading 1s cubic-bezier(0.3, 0, 0.7, 1) infinite;
        }

        .loadingCSSIcon div:nth-child(1) {
            background: #e15b64;
            animation-delay: -0.4s
        }

        .loadingCSSIcon div:nth-child(2) {
            background: #f8b26a;
            animation-delay: -0.2s
        }

        .loadingCSSIcon div:nth-child(3) {
            background: #99c959;
            animation-delay: 0s;
        }

        #loadMask>div>:first-child {
            font-size: 40px;
            color: var(--text-color);
        }

        #loadMask>div>:last-child {
            width: 140px;
            height: 70px;
            margin: 0 auto;
        }

        #voiceRec {
            position: absolute;
            right: 0;
            top: 0;
            width: 47px;
            height: 100%;
        }

        .message_if_voice {
            padding-right: 47px !important;
        }

        #voiceRecIcon {
            width: 100%;
            height: 100%;
            text-align: center;
            cursor: pointer;
            position: relative;
        }

        #voiceRecIcon:hover>svg {
            color: var(--svg-color);
        }

        #voiceRecIcon>svg {
            width: 25px;
            height: 25px;
            color: #b0b0b0;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-top: -12px;
            margin-left: -13px;
        }

        #voiceRecIcon>svg .animVoice {
            display: none;
        }

        .voiceRecing>svg {
            color: #99c959 !important;
        }

        .voiceRecing .animVoice {
            display: inline !important;
            transform-origin: 0 64%;
            animation-duration: 1.5s;
            animation-name: scaleVoice;
            animation-timing-function: ease;
            animation-iteration-count: infinite;
        }

        .voiceLong .animVoice {
            display: inline !important;
            transform-origin: 0 64%;
            animation-duration: 0.3s;
            animation-name: longVoice;
            animation-timing-function: ease-in-out;
            animation-iteration-count: 1;
        }

        @keyframes longVoice {
            0% {
                transform: scaleY(0);
            }

            100% {
                transform: scaleY(1);
            }
        }

        @keyframes scaleVoice {
            0% {
                transform: scaleY(0.28);
            }

            20% {
                transform: scaleY(0.60);
            }

            28% {
                transform: scaleY(0.28);
            }

            36% {
                transform: scaleY(0.45);
            }

            44% {
                transform: scaleY(0.28);
            }

            52% {
                transform: scaleY(0.45);
            }

            62% {
                transform: scaleY(0.80);
            }

            72% {
                transform: scaleY(0.80);
            }

            90% {
                transform: scaleY(0.28);
            }

            100% {
                transform: scaleY(0.28);
            }
        }

        #voiceRecSetting {
            display: none;
            position: absolute;
            top: -70px;
            left: -26px;
            z-index: 1;
            padding: 4px 4px;
            -webkit-user-select: none;
            user-select: none;
            border-radius: 3px;
            background-color: var(--main-back);
            box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
        }

        #voiceRecSetting select {
            width: 102px;
            outline: none;
            height: 28px;
            border-radius: 3px;
            border-color: rgba(0, 0, 0, .3);
        }

        .presetModelCls label {
            margin-right: 8px;
        }

        .presetModelCls select {
            height: 30px;
            margin-top: 2px;
            font-size: 15px;
        }

        .modelSwitch,
        .setSwitch {
            display: flex;
        }

        .modelSwitch {
            margin-bottom: 10px;
            -webkit-user-select: none;
            user-select: none;
        }

        .modelSwitch>div,
        .setSwitch>div {
            border-radius: 3px;
            width: calc(100% / 3);
            height: 32px;
            line-height: 32px;
            text-align: center;
            cursor: pointer;
        }

        .modelSwitch>div:hover,
        .setSwitch>div:hover {
            background-color: var(--active-btn);
        }

        .activeSwitch {
            background-color: var(--sel-btn) !important;
        }

        .checkLoad {
            height: 32px;
            border-radius: 3px;
            line-height: 32px;
            background: var(--sel-btn);
            text-align: center;
            display: flex;
            justify-content: center;
            cursor: pointer;
        }

        .checkLoad:hover {
            background: var(--lighter-svg-color);
        }

        .voiceChecking {
            background-color: var(--lighter-svg-color) !important;
        }

        .voiceChecking>svg {
            display: inline !important;
        }

        .checkLoad>svg {
            display: none;
            margin-right: 8px;
            height: 32px;
            width: 64px;
        }

        #preSetSystem {
            height: 20px;
            line-height: 20px;
            vertical-align: top;
        }

        #sysMask {
            display: none;
            position: fixed;
            z-index: 200;
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
            cursor: pointer;
            justify-content: center;
            align-items: center;
            background: rgba(0, 0, 0, .4);
            -webkit-tap-highlight-color: transparent;
        }

        #sysDialog {
            position: relative;
            background: var(--chat-back);
            color: var(--btn-color);
            cursor: auto;
            max-height: 100%;
            width: 88%;
            display: flex;
            flex-direction: column;
            border-radius: 4px;
            padding: 12px 20px 12px 20px;
        }

        .sysTitle {
            font-size: 20px;
            font-weight: bold;
            margin-bottom: 8px;
            -webkit-user-select: none;
            user-select: none;
        }

        .sysSwitch,
        .sysSwitch>div * {
            pointer-events: none;
        }

        .sysSwitch>div {
            border-radius: 3px;
            height: 32px;
            line-height: 32px;
            text-align: center;
            cursor: pointer;
            pointer-events: auto;
            font-weight: bold;
            display: flex;
            align-items: center;
            -webkit-user-select: none;
            user-select: none;
        }

        .sysSwitch>div>svg {
            margin-right: 4px;
        }

        .sysSwitch>div:hover {
            background-color: var(--active-btn);
        }

        .sysDetail {
            flex: 1;
        }

        #closeSet {
            position: absolute;
            right: 0px;
            top: 0px;
            cursor: pointer;
            padding: 10px 14px;
        }

        #closeSet:hover {
            color: var(--black-color);
        }

        .setContent {
            margin-bottom: 10px;
        }

        .setNotNormalFlow {
            position: relative;
        }

        .setTitle {
            margin-bottom: 6px;
            font-weight: bold;
            -webkit-user-select: none;
            user-select: none;
        }

        .setDetail {
            margin: 0 10px;
            -webkit-user-select: none;
            user-select: none;
        }

        .autoSelect>label,
        .autoSelect>input {
            cursor: pointer;
        }

        .dataDetail {
            display: flex;
        }

        .dataDetail svg {
            margin-right: 4px;
        }

        .dataDetail>div,
        .dataDetail>label {
            border-radius: 3px;
            text-align: center;
            padding: 6px 8px;
            margin-right: 12px;
            color: var(--btn-color);
            font-size: 15px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .dataDetail>div:hover,
        .dataDetail>label:hover {
            background: var(--lighter-active-color);
        }

        .keyOptionDetail {
            display: flex;
            margin-top: 10px;
        }

        #resetHotKey {
            display: flex;
            align-items: center;
            justify-content: space-between;
            cursor: pointer;
            font-size: 15px;
            color: var(--btn-color);
            border-radius: 3px;
            padding: 6px 8px;
            margin-left: 90px;
        }

        #resetHotKey>svg {
            margin-right: 4px;
        }

        #resetHotKey:hover {
            background: var(--lighter-active-color);
        }

        .hotKeyDetail>div {
            position: relative;
            height: 30px;
            margin-bottom: 2px;
        }

        .hotKeyDetail label {
            line-height: 30px;
        }

        .hotKeyDetail select {
            position: absolute;
            left: 110px;
            outline: none;
            border-radius: 3px;
            width: 120px;
            border-color: rgba(0, 0, 0, .3);
            background: var(--chat-back);
            color: var(--chat-text-color);
            height: 30px;
            font-size: 15px;
        }

        .avatarDetail {
            display: flex;
            margin-top: 2px;
        }

        .avatarDetail img {
            border-radius: 2px;
            width: 32px;
            height: 32px;
            flex-shrink: 0;
            margin-right: 8px;
            margin-top: 2px;
        }

        .inputDetail input {
            outline: none;
            border-radius: 3px;
            padding-left: 8px;
            font-size: 15px;
            width: 100%;
            height: 34px;
            border: 1px solid rgba(0, 0, 0, .3);
            background: var(--chat-back);
            color: var(--chat-text-color);
        }

        .themeDetail {
            display: flex;
            width: 180px;
            justify-content: space-between;
            pointer-events: none;
        }

        .themeDetail svg {
            display: block;
        }

        .themeDetail>div {
            pointer-events: auto;
            border-radius: 20px;
            text-align: center;
            padding: 8px 8px;
            color: var(--btn-color);
            font-size: 14px;
            cursor: pointer;
        }

        .themeDetail>div * {
            pointer-events: none;
        }

        .themeDetail>div:hover {
            background: var(--lighter-active-color);
        }

        .darkTheme>div:first-child {
            background: var(--sel-btn);
        }

        .lightTheme>div:nth-child(2) {
            background: var(--sel-btn);
        }

        .autoTheme>div:nth-child(3) {
            background: var(--sel-btn);
        }

        .langDetail {
            width: 110px;
        }

        .enLang>div:first-child {
            background: var(--sel-btn);
        }

        .zhLang>div:nth-child(2) {
            background: var(--sel-btn);
        }

        #customAutoSet input {
            width: 100px;
            height: 30px;
            line-height: 30px;
            font-size: 15px;
            outline: none;
            border: 1px solid rgba(0, 0, 0, .3);
            text-align: center;
            border-radius: 3px;
            background: var(--chat-back);
            color: var(--chat-text-color);
        }

        #customAutoSet label {
            margin-right: 8px;
        }

        .progressBar {
            position: relative;
            width: 100%;
            height: 12px;
            border-radius: 6px;
            background: var(--active-btn);
            overflow: hidden;
        }

        .nowProgress {
            position: absolute;
            left: 0;
            top: 0;
            height: 12px;
            min-width: 1px;
            border-radius: 6px;
            background: #99c959;
        }

        .progressDetail {
            display: flex;
            justify-content: space-between;
            font-size: 15px;
        }

        .cursorCls {
            background: var(--chat-text-color);
            width: 4px;
            animation: 1s cursor-blinker infinite step-start;
        }

        @keyframes cursor-blinker {
            0% {
                opacity: 0;
            }

            50% {
                opacity: 1;
            }

            100% {
                opacity: 0;
            }
        }

        .apiSelsContainer {
            position: relative;
            z-index: 1;
        }

        #apiSelect {
            position: absolute;
            top: 64px;
            padding: 4px 0;
            background: var(--chat-back);
            width: calc(100% - 20px);
            left: 10px;
            box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
            max-height: 180px;
            overflow-y: auto;
            -webkit-user-select: none;
            user-select: none;
        }

        #apiSelect>div {
            pointer-events: auto;
            cursor: pointer;
            font-size: 15px;
            padding: 6px 0 6px 8px;
            height: 36px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        #apiSelect>div:hover {
            background: var(--lighter-active-color);
        }

        #apiSelect>div>span {
            height: 100%;
            line-height: 23px;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }

        #apiSelect>div * {
            pointer-events: none;
        }

        .delApiOption:hover {
            background: var(--lighter-svg-color);
        }

        .delApiOption {
            width: 36px;
            height: 36px;
            flex-shrink: 0;
            pointer-events: auto !important;
        }

        .delApiOption>svg {
            margin: 6px;
            display: block;
        }

        @keyframes textInOut {
            from {
                opacity: 0;
            }

            to {
                opacity: 1;
            }
        }

        .response[data-loading="true"]>.responseBody>*:not(.cursorCls) {
            animation: textInOut 0.48s ease-in-out forwards;
        }

        .cotBtn {
            width: 120px;
            height: 32px;
            font-size: 14px;
            user-select: none;
            background-color: var(--lighter-active-color);
            position: relative;
            padding-left: 14px;
            line-height: 32px;
            border-radius: 8px;
            margin-bottom: 12px;
            cursor: pointer;
        }

        .cotBtn:hover {
            background: var(--active-btn);
        }

        .cotBtn.endCot {
            width: 142px;
        }

        .cotBtn::after {
            content: '';
            display: inline-block;
            position: absolute;
            right: 15px;
            top: 14px;
            width: 6px;
            height: 6px;
            border-top: 1px solid var(--chat-text-color);
            border-right: 1px solid var(--chat-text-color);
            transform: rotate(-45deg);
        }

        .cotBtn.down::after {
            top: 11px;
            transform: rotate(135deg);
        }

        .cotContent {
            border-left: 2px solid var(--lighter-active-color);
            padding-left: 12px;
            font-size: 14px;
            color: var(--svg-color);
        }

        .cotContent>:last-child {
            margin-bottom: 12px !important;
        }

        .hide {
            display: none;
        }
    </style>
    <style>
        /* for katex */
        .katex {
            font-size: 1em !important;
        }

        eq {
            display: inline-block;
        }

        eqn {
            display: block
        }

        section.eqno {
            display: flex;
            flex-direction: row;
            align-content: space-between;
            align-items: center;
        }

        section.eqno>eqn {
            width: 100%;
            margin-left: 3em;
        }

        section.eqno>span {
            width: 3em;
            text-align: right;
        }
    </style>
    <script>
        let themeMode; // 2: 自动， 1: 浅色，0: 深色
        let autoThemeMode; // 1: 跟随系统，0:自定义时间
        let customDarkTime; // 开始，结束时间
        let isFull = false; // 是否全屏
        const darkMedia = window.matchMedia("(prefers-color-scheme: dark)");
        const justDarkTheme = (is) => {
            if (is) document.documentElement.setAttribute("data-theme", "dark");
            else document.documentElement.removeAttribute("data-theme");
            document.head.children[4].content = document.head.children[5].content = document.head.children[6].content = getComputedStyle(document.documentElement).getPropertyValue("--background");
        }
        const checkDark = () => {
            const checkCustomTheme = () => {
                let date = new Date();
                let nowTime = date.getTime();
                let start = customDarkTime[0].split(":");
                let startTime = new Date().setHours(start[0], start[1], 0, 0);
                let end = customDarkTime[1].split(":");
                let endTime = new Date().setHours(end[0], end[1], 0, 0);
                let order = endTime > startTime;
                let isDark = order ? (nowTime > startTime && endTime > nowTime) : !(nowTime > endTime && startTime > nowTime);
                justDarkTheme(isDark);
            }
            const setDarkMode = () => {
                if (themeMode === 2) {
                    if (autoThemeMode) {
                        justDarkTheme(darkMedia.matches);
                    } else {
                        checkCustomTheme();
                    }
                } else if (themeMode === 1) {
                    justDarkTheme(false);
                } else {
                    justDarkTheme(true);
                }
                localStorage.setItem("themeMode", themeMode);
            }
            let localTheme = localStorage.getItem("themeMode");
            themeMode = parseInt(localTheme || "1");
            let localAutoTheme = localStorage.getItem("autoThemeMode");
            autoThemeMode = parseInt(localAutoTheme || "1");
            let localCustomDark = localStorage.getItem("customDarkTime");
            customDarkTime = JSON.parse(localCustomDark || '["21:00", "07:00"]');
            setDarkMode();
        }
        checkDark();
    </script>
</head>

<body>
    <svg style="transform: translate(-1000px, -1000px);">
        <defs>
            <radialGradient id="paint0_radial_16771_53212" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
                gradientTransform="translate(2.77876 11.3795) rotate(18.6832) scale(29.8025 238.737)">
                <stop offset="0.0671246" stop-color="#9168C0" />
                <stop offset="0.342551" stop-color="#5684D1" />
                <stop offset="0.672076" stop-color="#1BA1E3" />
            </radialGradient>
        </defs>
        <defs>
            <style>
                .cls-1 {
                    fill: none;
                }

                .cls-1,
                .cls-2,
                .cls-3,
                .cls-4 {
                    stroke-width: 0px;
                }

                .cls-2 {
                    fill: url(#linear-gradient);
                }

                .cls-2,
                .cls-3 {
                    fill-rule: evenodd;
                }

                .cls-3 {
                    fill: url(#linear-gradient-3);
                }

                .cls-4 {
                    fill: url(#linear-gradient-2);
                }
            </style>
            <linearGradient id="linear-gradient" x1="12.76" y1="11.97" x2="10.26" y2="1.96"
                gradientUnits="userSpaceOnUse">
                <stop offset="0" stop-color="#712575" />
                <stop offset=".09" stop-color="#9a2884" />
                <stop offset=".18" stop-color="#bf2c92" />
                <stop offset=".27" stop-color="#da2e9c" />
                <stop offset=".34" stop-color="#eb30a2" />
                <stop offset=".4" stop-color="#f131a5" />
                <stop offset=".5" stop-color="#ec30a3" />
                <stop offset=".61" stop-color="#df2f9e" />
                <stop offset=".72" stop-color="#c92d96" />
                <stop offset=".83" stop-color="#aa2a8a" />
                <stop offset=".95" stop-color="#83267c" />
                <stop offset="1" stop-color="#712575" />
            </linearGradient>
            <linearGradient id="linear-gradient-2" x1="13.71" y1="1.8" x2="13.71" y2="15.92"
                gradientUnits="userSpaceOnUse">
                <stop offset="0" stop-color="#da7ed0" />
                <stop offset=".08" stop-color="#b17bd5" />
                <stop offset=".19" stop-color="#8778db" />
                <stop offset=".3" stop-color="#6276e1" />
                <stop offset=".41" stop-color="#4574e5" />
                <stop offset=".54" stop-color="#2e72e8" />
                <stop offset=".67" stop-color="#1d71eb" />
                <stop offset=".81" stop-color="#1471ec" />
                <stop offset="1" stop-color="#1171ed" />
            </linearGradient>
            <linearGradient id="linear-gradient-3" x1="12.86" y1="2.12" x2="3.5" y2="17.13"
                gradientUnits="userSpaceOnUse">
                <stop offset="0" stop-color="#da7ed0" />
                <stop offset=".05" stop-color="#b77bd4" />
                <stop offset=".11" stop-color="#9079da" />
                <stop offset=".18" stop-color="#6e77df" />
                <stop offset=".25" stop-color="#5175e3" />
                <stop offset=".33" stop-color="#3973e7" />
                <stop offset=".42" stop-color="#2772e9" />
                <stop offset=".54" stop-color="#1a71eb" />
                <stop offset=".68" stop-color="#1371ec" />
                <stop offset="1" stop-color="#1171ed" />
            </linearGradient>
        </defs>
    </svg>
    <div style="display: none">
        <svg>
            <symbol viewBox="299 299 1808 1808" id="aiIcon">
                <path fill="currentColor"
                    d="M1107.3 299.1c-198 0-373.9 127.3-435.2 315.3C544.8 640.6 434.9 720.2 370.5 833c-99.3 171.4-76.6 386.9 56.4 533.8-41.1 123.1-27 257.7 38.6 369.2 98.7 172 297.3 260.2 491.6 219.2 86.1 97 209.8 152.3 339.6 151.8 198 0 373.9-127.3 435.3-315.3 127.5-26.3 237.2-105.9 301-218.5 99.9-171.4 77.2-386.9-55.8-533.9v-.6c41.1-123.1 27-257.8-38.6-369.8-98.7-171.4-297.3-259.6-491-218.6-86.6-96.8-210.5-151.8-340.3-151.2zm0 117.5-.6.6c79.7 0 156.3 27.5 217.6 78.4-2.5 1.2-7.4 4.3-11 6.1L952.8 709.3c-18.4 10.4-29.4 30-29.4 51.4V1248l-155.1-89.4V755.8c-.1-187.1 151.6-338.9 339-339.2zm434.2 141.9c121.6-.2 234 64.5 294.7 169.8 39.2 68.6 53.9 148.8 40.4 226.5-2.5-1.8-7.3-4.3-10.4-6.1l-360.4-208.2c-18.4-10.4-41-10.4-59.4 0L1024 984.2V805.4L1372.7 604c51.3-29.7 109.5-45.4 168.8-45.5zM650 743.5v427.9c0 21.4 11 40.4 29.4 51.4l421.7 243-155.7 90L597.2 1355c-162-93.8-217.4-300.9-123.8-462.8C513.1 823.6 575.5 771 650 743.5zm807.9 106 348.8 200.8c162.5 93.7 217.6 300.6 123.8 462.8l.6.6c-39.8 68.6-102.4 121.2-176.5 148.2v-428c0-21.4-11-41-29.4-51.4l-422.3-243.7 155-89.3zM1201.7 997l177.8 102.8v205.1l-177.8 102.8-177.8-102.8v-205.1L1201.7 997zm279.5 161.6 155.1 89.4v402.2c0 187.3-152 339.2-339 339.2v-.6c-79.1 0-156.3-27.6-217-78.4 2.5-1.2 8-4.3 11-6.1l360.4-207.5c18.4-10.4 30-30 29.4-51.4l.1-486.8zM1380 1421.9v178.8l-348.8 200.8c-162.5 93.1-369.6 38-463.4-123.7h.6c-39.8-68-54-148.8-40.5-226.5 2.5 1.8 7.4 4.3 10.4 6.1l360.4 208.2c18.4 10.4 41 10.4 59.4 0l421.9-243.7z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="deepseekIcon">
                <path
                    d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"
                    fill="#4D6BFE" />
            </symbol>
            <symbol viewBox="0 0 18 18" id="azureAIIcon">
                <path class="cls-2" d="m11.52,1.59c.44,0,.83.33.97.81s.96,3.45.96,3.45v5.9h-2.97l.06-10.17h.98Z" />
                <path class="cls-4"
                    d="m15.88,6.2c0-.21-.17-.37-.37-.37h-1.75c-1.23,0-2.23,1-2.23,2.23v3.7h2.12c1.23,0,2.23-1,2.23-2.23v-3.33Z" />
                <path class="cls-3"
                    d="m11.52,1.59c-.34,0-.61.27-.61.61l-.06,11.23c0,1.64-1.33,2.97-2.97,2.97H2.49c-.26,0-.43-.25-.35-.49L6.46,3.58c.42-1.19,1.54-1.99,2.8-1.99h2.27Z" />
                <rect class="cls-1" width="18" height="18" />
            </symbol>
            <symbol viewBox="0 0 28 28" id="geminiIcon">
                <path
                    d="M14 28C14 26.0633 13.6267 24.2433 12.88 22.54C12.1567 20.8367 11.165 19.355 9.905 18.095C8.645 16.835 7.16333 15.8433 5.46 15.12C3.75667 14.3733 1.93667 14 0 14C1.93667 14 3.75667 13.6383 5.46 12.915C7.16333 12.1683 8.645 11.165 9.905 9.905C11.165 8.645 12.1567 7.16333 12.88 5.46C13.6267 3.75667 14 1.93667 14 0C14 1.93667 14.3617 3.75667 15.085 5.46C15.8317 7.16333 16.835 8.645 18.095 9.905C19.355 11.165 20.8367 12.1683 22.54 12.915C24.2433 13.6383 26.0633 14 28 14C26.0633 14 24.2433 14.3733 22.54 15.12C20.8367 15.8433 19.355 16.835 18.095 18.095C16.835 19.355 15.8317 20.8367 15.085 22.54C14.3617 24.2433 14 26.0633 14 28Z"
                    fill="url(#paint0_radial_16771_53212)" />
            </symbol>
            <symbol viewBox="0 0 40 40" id="claudeIcon">
                <path shape-rendering="optimizeQuality" fill="#D97757"
                    d="m7.75 26.27 7.77-4.36.13-.38-.13-.21h-.38l-1.3-.08-4.44-.12-3.85-.16-3.73-.2-.94-.2L0 19.4l.09-.58.79-.53 1.13.1 2.5.17 3.75.26 2.72.16 4.03.42h.64l.09-.26-.22-.16-.17-.16-3.88-2.63-4.2-2.78-2.2-1.6L3.88 11l-.6-.76-.26-1.66L4.1 7.39l1.45.1.37.1 1.47 1.13 3.14 2.43 4.1 3.02.6.5.24-.17.03-.12-.27-.45L13 9.9l-2.38-4.1-1.06-1.7-.28-1.02c-.1-.42-.17-.77-.17-1.2L10.34.21l.68-.22 1.64.22.69.6 1.02 2.33 1.65 3.67 2.56 4.99.75 1.48.4 1.37.15.42h.26v-.24l.21-2.81.39-3.45.38-4.44.13-1.25.62-1.5L23.1.57l.96.46.79 1.13-.11.73-.47 3.05-.92 4.78-.6 3.2h.35l.4-.4 1.62-2.15 2.72-3.4 1.2-1.35 1.4-1.49.9-.71h1.7l1.25 1.86-.56 1.92-1.75 2.22-1.45 1.88-2.08 2.8-1.3 2.24.12.18.31-.03 4.7-1 2.54-.46 3.03-.52 1.37.64.15.65-.54 1.33-3.24.8-3.8.76-5.66 1.34-.07.05.08.1 2.55.24 1.09.06h2.67l4.97.37 1.3.86.78 1.05-.13.8-2 1.02-2.7-.64-6.3-1.5-2.16-.54h-.3v.18l1.8 1.76 3.3 2.98 4.13 3.84.21.95-.53.75-.56-.08-3.63-2.73-1.4-1.23-3.17-2.67h-.21v.28l.73 1.07 3.86 5.8.2 1.78-.28.58-1 .35-1.1-.2L26 33.14l-2.33-3.57-1.88-3.2-.23.13-1.11 11.95-.52.61-1.2.46-1-.76-.53-1.23.53-2.43.64-3.17.52-2.52.47-3.13.28-1.04-.02-.07-.23.03-2.36 3.24-3.59 4.85-2.84 3.04-.68.27-1.18-.61.11-1.09.66-.97 3.93-5 2.37-3.1 1.53-1.79-.01-.26h-.09L6.8 30.56l-1.86.24-.8-.75.1-1.23.38-.4 3.14-2.16Z">
                </path>
            </symbol>
            <symbol viewBox="0 0 16 16" id="expandIcon">
                <path fill="currentColor"
                    d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="refreshIcon">
                <path fill="currentColor"
                    d="M3.07 10.876C3.623 6.436 7.41 3 12 3a9.15 9.15 0 0 1 6.012 2.254V4a1 1 0 1 1 2 0v4a1 1 0 0 1-1 1H15a1 1 0 1 1 0-2h1.957A7.15 7.15 0 0 0 12 5a7 7 0 0 0-6.946 6.124 1 1 0 1 1-1.984-.248m16.992 1.132a1 1 0 0 1 .868 1.116C20.377 17.564 16.59 21 12 21a9.15 9.15 0 0 1-6-2.244V20a1 1 0 1 1-2 0v-4a1 1 0 0 1 1-1h4a1 1 0 1 1 0 2H7.043A7.15 7.15 0 0 0 12 19a7 7 0 0 0 6.946-6.124 1 1 0 0 1 1.116-.868">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="halfRefIcon">
                <path fill="currentColor"
                    d="M3.07 10.876C3.623 6.436 7.41 3 12 3a9.15 9.15 0 0 1 6.012 2.254V4a1 1 0 1 1 2 0v4a1 1 0 0 1-1 1H15a1 1 0 1 1 0-2h1.957A7.15 7.15 0 0 0 12 5a7 7 0 0 0-6.946 6.124 1 1 0 1 1-1.984-.248" />
            </symbol>
            <symbol viewBox="-2 -2 20 20" id="copyIcon">
                <path fill="currentColor"
                    d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z">
                </path>
                <path fill="currentColor"
                    d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="delIcon">
                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M9 7v0a3 3 0 0 1 3-3v0a3 3 0 0 1 3 3v0M9 7h6M9 7H6m9 0h3m2 0h-2M4 7h2m0 0v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="readyVoiceIcon">
                <path fill="currentColor"
                    d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z">
                </path>
            </symbol>
            <symbol viewBox="0 0 20 20" id="pauseVoiceIcon">
                <path stroke="currentColor" stroke-width="2.4" d="M6 3v14M14 3v14"></path>
            </symbol>
            <symbol viewBox="0 0 16 16" id="resumeVoiceIcon">
                <path fill="currentColor" d="M4 3L4 13L12 8Z"></path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="stopResIcon">
                <path fill="currentColor"
                    d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10zm0-2a8 8 0 1 0 0-16a8 8 0 0 0 0 16zM9 9h6v6H9V9z">
                </path>
            </symbol>
            <symbol viewBox="0 0 128 128" id="downAudioIcon">
                <path
                    d="M 64.662 1.549 C 56.549 4.524, 46.998 14.179, 45.523 20.895 C 45.041 23.089, 44.073 23.833, 40.433 24.807 C 34.752 26.326, 27.956 32.929, 25.527 39.289 C 24.273 42.574, 23.884 45.715, 24.196 50.034 C 24.620 55.897, 24.528 56.193, 21.836 57.585 C 17.142 60.012, 16 63.617, 16 76 C 16 88.463, 17.137 91.985, 21.967 94.483 C 28.244 97.729, 36.120 95.350, 38.579 89.466 C 39.387 87.532, 40 82.764, 40 78.415 C 40 70.971, 40.060 70.783, 42.250 71.370 C 43.487 71.701, 48.888 71.979, 54.250 71.986 L 64 72 64 76 L 64 80 57.122 80 C 49.420 80, 48.614 80.543, 47.547 86.453 C 46.552 91.964, 43.550 97.473, 40.273 99.803 C 33 104.974, 23.120 105.042, 16.118 99.971 C 11.407 96.558, 9.048 92.484, 8.145 86.205 C 6.963 77.979, 0.794 77.729, 0.191 85.883 C -0.196 91.111, 3.323 99.170, 8.062 103.908 C 11.290 107.136, 20.073 111.969, 22.750 111.990 C 23.540 111.996, 24 113.472, 24 116 C 24 119.740, 23.813 120, 21.122 120 C 17.674 120, 15.727 122.044, 16.173 125.195 C 16.492 127.441, 16.781 127.500, 27.391 127.500 C 36.676 127.500, 38.445 127.242, 39.386 125.750 C 40.993 123.203, 38.986 120.568, 35.149 120.187 C 32.206 119.894, 32 119.617, 32 115.956 C 32 112.509, 32.330 111.959, 34.750 111.377 C 42.181 109.591, 52.157 101.208, 53.575 95.559 C 53.928 94.152, 54.514 93, 54.878 93 C 55.242 93, 59.797 97.275, 65 102.500 C 70.762 108.286, 75.256 112, 76.495 112 C 77.769 112, 83.287 107.231, 91.264 99.236 C 101.113 89.366, 104 85.876, 104 83.843 C 104 80.580, 102.553 80, 94.418 80 L 88 80 88 76.105 L 88 72.211 99.750 71.815 C 113.117 71.364, 117.595 69.741, 122.762 63.473 C 128.159 56.925, 129.673 45.269, 126.134 37.500 C 123.787 32.346, 117.218 26.445, 112.132 24.921 C 108.617 23.868, 107.767 22.968, 105.028 17.405 C 99.364 5.901, 89.280 -0.062, 75.712 0.070 C 71.746 0.109, 66.773 0.774, 64.662 1.549 M 67.885 9.380 C 60.093 12.164, 55.057 17.704, 52.527 26.276 C 51.174 30.856, 50.220 31.617, 44.729 32.496 C 37.017 33.729, 30.917 42.446, 32.374 50.154 C 34.239 60.026, 40.582 63.944, 54.750 63.978 L 64 64 64 57.122 C 64 52.457, 64.449 49.872, 65.396 49.086 C 66.310 48.328, 70.370 48.027, 77.146 48.214 L 87.500 48.500 87.794 56.359 L 88.088 64.218 98.989 63.845 C 108.043 63.535, 110.356 63.125, 112.634 61.424 C 119.736 56.122, 121.911 47.667, 118.097 40.190 C 115.870 35.824, 110.154 32.014, 105.790 31.985 C 102.250 31.961, 101.126 30.787, 99.532 25.443 C 95.580 12.197, 80.880 4.736, 67.885 9.380 M 72 70.800 C 72 80.978, 71.625 85.975, 70.800 86.800 C 70.140 87.460, 67.781 88, 65.559 88 L 61.517 88 68.759 95.241 L 76 102.483 83.241 95.241 L 90.483 88 86.441 88 C 84.219 88, 81.860 87.460, 81.200 86.800 C 80.375 85.975, 80 80.978, 80 70.800 L 80 56 76 56 L 72 56 72 70.800 M 25.200 65.200 C 23.566 66.834, 23.566 85.166, 25.200 86.800 C 27.002 88.602, 29.798 88.246, 30.965 86.066 C 31.534 85.002, 32 80.472, 32 76 C 32 71.528, 31.534 66.998, 30.965 65.934 C 29.798 63.754, 27.002 63.398, 25.200 65.200"
                    stroke="none" fill="currentColor" fill-rule="evenodd" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="chatIcon">
                <path fill="currentColor"
                    d="m18 21l-1.4-1.4l1.575-1.6H14v-2h4.175L16.6 14.4L18 13l4 4l-4 4ZM3 21V6q0-.825.588-1.413T5 4h12q.825 0 1.413.588T19 6v5.075q-.25-.05-.5-.063T18 11q-.25 0-.5.013t-.5.062V6H5v10h7.075q-.05.25-.063.5T12 17q0 .25.013.5t.062.5H6l-3 3Zm4-11h8V8H7v2Zm0 4h5v-2H7v2Zm-2 2V6v10Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="chatEditIcon">
                <path fill="currentColor"
                    d="M5 19h1.4l8.625-8.625l-1.4-1.4L5 17.6V19ZM19.3 8.925l-4.25-4.2l1.4-1.4q.575-.575 1.413-.575t1.412.575l1.4 1.4q.575.575.6 1.388t-.55 1.387L19.3 8.925ZM17.85 10.4L7.25 21H3v-4.25l10.6-10.6l4.25 4.25Zm-3.525-.725l-.7-.7l1.4 1.4l-.7-.7Z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="deleteIcon">
                <path fill="currentColor"
                    d="M8 20v-5h2v5h9v-7H5v7h3zm-4-9h16V8h-6V4h-4v4H4v3zM3 21v-8H2V7a1 1 0 0 1 1-1h5V3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v3h5a1 1 0 0 1 1 1v6h-1v8a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="addIcon" stroke="currentColor" fill="none" stroke-width="2"
                stroke-linecap="round" stroke-linejoin="round">
                <line x1="12" y1="5" x2="12" y2="19"></line>
                <line x1="5" y1="12" x2="19" y2="12"></line>
            </symbol>
            <symbol viewBox="0 0 200 100" preserveAspectRatio="xMidYMid" id="loadingIcon">
                <g transform="translate(50 50)">
                    <circle cx="0" cy="0" r="15" fill="#e15b64">
                        <animateTransform attributeName="transform" type="scale" begin="-0.4s" calcMode="spline"
                            keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s"
                            repeatCount="indefinite"></animateTransform>
                    </circle>
                </g>
                <g transform="translate(100 50)">
                    <circle cx="0" cy="0" r="15" fill="#f8b26a">
                        <animateTransform attributeName="transform" type="scale" begin="-0.2s" calcMode="spline"
                            keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s"
                            repeatCount="indefinite"></animateTransform>
                    </circle>
                </g>
                <g transform="translate(150 50)">
                    <circle cx="0" cy="0" r="15" fill="#99c959">
                        <animateTransform attributeName="transform" type="scale" begin="0s" calcMode="spline"
                            keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s"
                            repeatCount="indefinite"></animateTransform>
                    </circle>
                </g>
            </symbol>
            <symbol viewBox="0 0 24 24" id="exportIcon">
                <path fill="currentColor"
                    d="m17.86 18l1.04 1c-1.4 1.2-3.96 2-6.9 2c-4.41 0-8-1.79-8-4V7c0-2.21 3.58-4 8-4c2.95 0 5.5.8 6.9 2l-1.04 1l-.36.4C16.65 5.77 14.78 5 12 5C8.13 5 6 6.5 6 7s2.13 2 6 2c1.37 0 2.5-.19 3.42-.46l.96.96H13.5v1.42c-.5.05-1 .08-1.5.08c-2.39 0-4.53-.53-6-1.36v2.81C7.3 13.4 9.58 14 12 14c.5 0 1-.03 1.5-.08v.58h2.88l-1 1l.12.11c-1.09.25-2.26.39-3.5.39c-2.28 0-4.39-.45-6-1.23V17c0 .5 2.13 2 6 2c2.78 0 4.65-.77 5.5-1.39l.36.39m1.06-10.92L17.5 8.5L20 11h-5v2h5l-2.5 2.5l1.42 1.42L23.84 12l-4.92-4.92Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="importIcon">
                <path fill="currentColor"
                    d="m8.84 12l-4.92 4.92L2.5 15.5L5 13H0v-2h5L2.5 8.5l1.42-1.42L8.84 12M12 3C8.59 3 5.68 4.07 4.53 5.57L5 6l1.03 1.07C6 7.05 6 7 6 7c0-.5 2.13-2 6-2s6 1.5 6 2s-2.13 2-6 2c-2.62 0-4.42-.69-5.32-1.28l3.12 3.12c.7.1 1.44.16 2.2.16c2.39 0 4.53-.53 6-1.36v2.81c-1.3.95-3.58 1.55-6 1.55c-.96 0-1.9-.1-2.76-.27l-1.65 1.64c1.32.4 2.82.63 4.41.63c2.28 0 4.39-.45 6-1.23V17c0 .5-2.13 2-6 2s-6-1.5-6-2v-.04L5 18l-.46.43C5.69 19.93 8.6 21 12 21c4.41 0 8-1.79 8-4V7c0-2.21-3.58-4-8-4Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="clearAllIcon">
                <path fill="currentColor"
                    d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10zm0-2a8 8 0 1 0 0-16a8 8 0 0 0 0 16zm0-9.414l2.828-2.829l1.415 1.415L13.414 12l2.829 2.828l-1.415 1.415L12 13.414l-2.828 2.829l-1.415-1.415L10.586 12L7.757 9.172l1.415-1.415L12 10.586z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="collapseFullIcon">
                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
                    stroke-width="1.5"
                    d="m20 20l-5-5m0 0v4m0-4h4M4 20l5-5m0 0v4m0-4H5M20 4l-5 5m0 0V5m0 4h4M4 4l5 5m0 0V5m0 4H5" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="expandFullIcon">
                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
                    stroke-width="1.5"
                    d="M9 9L4 4m0 0v4m0-4h4m7 5l5-5m0 0v4m0-4h-4M9 15l-5 5m0 0v-4m0 4h4m7-5l5 5m0 0v-4m0 4h-4" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="darkThemeIcon">
                <path fill="currentColor"
                    d="M20.742 13.045a8.088 8.088 0 0 1-2.077.271c-2.135 0-4.14-.83-5.646-2.336a8.025 8.025 0 0 1-2.064-7.723A1 1 0 0 0 9.73 2.034a10.014 10.014 0 0 0-4.489 2.582c-3.898 3.898-3.898 10.243 0 14.143a9.937 9.937 0 0 0 7.072 2.93 9.93 9.93 0 0 0 7.07-2.929 10.007 10.007 0 0 0 2.583-4.491 1.001 1.001 0 0 0-1.224-1.224zm-2.772 4.301a7.947 7.947 0 0 1-5.656 2.343 7.953 7.953 0 0 1-5.658-2.344c-3.118-3.119-3.118-8.195 0-11.314a7.923 7.923 0 0 1 2.06-1.483 10.027 10.027 0 0 0 2.89 7.848 9.972 9.972 0 0 0 7.848 2.891 8.036 8.036 0 0 1-1.484 2.059z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="lightThemeIcon">
                <path fill="currentColor"
                    d="M6.993 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007S14.761 6.993 12 6.993 6.993 9.239 6.993 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19h2v3h-2zm0-17h2v3h-2zm-9 9h3v2h-3zm17 0h3v2h-3zM4.219 18.363l2.12-2.122 1.415 1.414-2.12 2.122zM16.24 6.344l2.122-2.122 1.414 1.414-2.122 2.122zM6.342 7.759 4.22 5.637l1.415-1.414 2.12 2.122zm13.434 10.605-1.414 1.414-2.122-2.122 1.414-1.414z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="autoThemeIcon">
                <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
                    <path d="M9.173 14.83a4 4 0 1 1 5.657-5.657" />
                    <path
                        d="m11.294 12.707l.174.247a7.5 7.5 0 0 0 8.845 2.492A9 9 0 0 1 5.642 18.36M3 12h1m8-9v1M5.6 5.6l.7.7M3 21L21 3" />
                </g>
            </symbol>
            <symbol viewBox="0 0 24 24" id="newFolderIcon">
                <path fill="currentColor"
                    d="M14 16h2v-2h2v-2h-2v-2h-2v2h-2v2h2v2ZM2 20V4h8l2 2h10v14H2Zm2-2h16V8h-8.825l-2-2H4v12Zm0 0V6v12Z" />
            </symbol>
            <symbol viewBox="0 0 20 20" id="expandFolderIcon">
                <path fill="currentColor"
                    d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z">
                </path>
            </symbol>
            <symbol viewBox="0 0 24 24" id="closeIcon">
                <path fill="currentColor"
                    d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z" />
            </symbol>
            <symbol viewBox="0 0 32 32" id="modelsIcon">
                <path fill="currentColor"
                    d="M28.447 16.106L23 13.381V7a1 1 0 0 0-.553-.894l-6-3a1 1 0 0 0-.894 0l-6 3A1 1 0 0 0 9 7v6.382l-5.447 2.723A1 1 0 0 0 3 17v7a1 1 0 0 0 .553.895l6 3a1 1 0 0 0 .894 0L16 25.118l5.553 2.777a1 1 0 0 0 .894 0l6-3A1 1 0 0 0 29 24v-7a1 1 0 0 0-.553-.895M21 13.381l-4 2v-4.764l4-2Zm-5-8.264L19.764 7L16 8.882L12.236 7Zm-5 3.5l4 2v4.764l-4-2ZM9 25.382l-4-2v-4.764l4 2Zm1-6.5L6.236 17L10 15.118L13.764 17Zm1 1.736l4-2v4.764l-4 2Zm10 4.764l-4-2v-4.764l4 2Zm1-6.5L18.236 17L22 15.118L25.764 17Zm5 4.5l-4 2v-4.764l4-2Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="settingIcon">
                <path fill="currentColor"
                    d="M13.85 22.25h-3.7c-.74 0-1.36-.54-1.45-1.27l-.27-1.89c-.27-.14-.53-.29-.79-.46l-1.8.72c-.7.26-1.47-.03-1.81-.65L2.2 15.53c-.35-.66-.2-1.44.36-1.88l1.53-1.19c-.01-.15-.02-.3-.02-.46 0-.15.01-.31.02-.46l-1.52-1.19c-.59-.45-.74-1.26-.37-1.88l1.85-3.19c.34-.62 1.11-.9 1.79-.63l1.81.73c.26-.17.52-.32.78-.46l.27-1.91c.09-.7.71-1.25 1.44-1.25h3.7c.74 0 1.36.54 1.45 1.27l.27 1.89c.27.14.53.29.79.46l1.8-.72c.71-.26 1.48.03 1.82.65l1.84 3.18c.36.66.2 1.44-.36 1.88l-1.52 1.19c.01.15.02.3.02.46s-.01.31-.02.46l1.52 1.19c.56.45.72 1.23.37 1.86l-1.86 3.22c-.34.62-1.11.9-1.8.63l-1.8-.72c-.26.17-.52.32-.78.46l-.27 1.91c-.1.68-.72 1.22-1.46 1.22zm-3.23-2h2.76l.37-2.55.53-.22c.44-.18.88-.44 1.34-.78l.45-.34 2.38.96 1.38-2.4-2.03-1.58.07-.56c.03-.26.06-.51.06-.78s-.03-.53-.06-.78l-.07-.56 2.03-1.58-1.39-2.4-2.39.96-.45-.35c-.42-.32-.87-.58-1.33-.77l-.52-.22-.37-2.55h-2.76l-.37 2.55-.53.21c-.44.19-.88.44-1.34.79l-.45.33-2.38-.95-1.39 2.39 2.03 1.58-.07.56a7 7 0 0 0-.06.79c0 .26.02.53.06.78l.07.56-2.03 1.58 1.38 2.4 2.39-.96.45.35c.43.33.86.58 1.33.77l.53.22.38 2.55z">
                </path>
                <circle fill="currentColor" cx="12" cy="12" r="3.5"></circle>
            </symbol>
            <symbol viewBox="0 0 24 24" id="uiIcon">
                <path fill="currentColor"
                    d="M2 5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-7v2h3a1 1 0 1 1 0 2H8a1 1 0 1 1 0-2h3v-2H4a2 2 0 0 1-2-2zm18 11V5H4v11z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="importSetIcon">
                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="m12 21l-8-4.5v-9L12 3l8 4.5V12m-8 0l8-4.5M12 12v9m0-9L4 7.5M22 18h-7m3-3l-3 3l3 3" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="exportSetIcon">
                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="m12 21l-8-4.5v-9L12 3l8 4.5V12m-8 0l8-4.5M12 12v9m0-9L4 7.5M15 18h7m-3-3l3 3l-3 3" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="databaseIcon">
                <path fill="currentColor"
                    d="M12 3C7.58 3 4 4.79 4 7v10c0 2.21 3.59 4 8 4s8-1.79 8-4V7c0-2.21-3.58-4-8-4m6 14c0 .5-2.13 2-6 2s-6-1.5-6-2v-2.23c1.61.78 3.72 1.23 6 1.23s4.39-.45 6-1.23V17m0-4.55c-1.3.95-3.58 1.55-6 1.55s-4.7-.6-6-1.55V9.64c1.47.83 3.61 1.36 6 1.36s4.53-.53 6-1.36v2.81M12 9C8.13 9 6 7.5 6 7s2.13-2 6-2s6 1.5 6 2s-2.13 2-6 2Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="stopIcon">
                <path fill="currentColor" d="M6 5h12a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="forceRefreshIcon">
                <path fill="currentColor"
                    d="M13.82 14H9.66c-.1-.66-.16-1.32-.16-2s.06-1.35.16-2h4.68c.09.65.16 1.32.16 2c0 .5-.04 1-.1 1.46c.6-.5 1.32-.89 2.1-1.14V12c0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2v.18c.7.17 1.35.45 1.95.82c.05-.32.05-.66.05-1c0-5.5-4.5-10-10-10C6.47 2 2 6.5 2 12s4.5 10 10 10c.34 0 .68 0 1-.05c-.41-.66-.71-1.4-.87-2.2c-.04.07-.08.14-.13.21c-.83-1.2-1.5-2.53-1.91-3.96h2.41c.31-.75.76-1.42 1.32-2m5.1-6h-2.95a15.65 15.65 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2s.06 1.34.14 2H4.26m.82 2H8c.35 1.25.8 2.45 1.4 3.56A8.008 8.008 0 0 1 5.08 16M8 8H5.08A7.923 7.923 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m12.83 7.67L22 14.5v4h-4l1.77-1.77A2.5 2.5 0 1 0 20 20h1.71A3.991 3.991 0 0 1 18 22.5c-2.21 0-4-1.79-4-4s1.79-4 4-4c1.11 0 2.11.45 2.83 1.17Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="hotkeyIcon">
                <g fill="none">
                    <path
                        d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />
                    <path fill="currentColor"
                        d="M18 3a3 3 0 0 1 2.995 2.824L21 6v12a3 3 0 0 1-2.824 2.995L18 21H6a3 3 0 0 1-2.995-2.824L3 18V6a3 3 0 0 1 2.824-2.995L6 3h12Zm-2.707 13.708A2.99 2.99 0 0 1 14 17H5v1a1 1 0 0 0 1 1h11.586l-2.293-2.292ZM18 5h-1v9c0 .386-.073.755-.206 1.094l-.086.2L19 17.585V6a1 1 0 0 0-.883-.993L18 5Zm-3 0H6a1 1 0 0 0-.993.883L5 6v9h9a1 1 0 0 0 .993-.883L15 14V5ZM9 7a1 1 0 0 1 .993.883L10 8v.631l1.445-.963a1 1 0 0 1 1.203 1.594l-.093.07l-1.377.918l1.377.918a1 1 0 0 1-1.009 1.723l-.1-.059L10 11.868V12a1 1 0 0 1-1.993.117L8 12V8a1 1 0 0 1 1-1Z" />
                </g>
            </symbol>
            <symbol viewBox="0 0 24 24" id="zhIcon">
                <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2">
                    <path stroke-linejoin="round" d="M5 8h14v7H5z" />
                    <path d="M12 4v17" />
                </g>
            </symbol>
            <symbol viewBox="0 0 24 24" id="enIcon">
                <path fill="currentColor"
                    d="M14 10h2v.757a4.5 4.5 0 0 1 7 3.743V20h-2v-5.5c0-1.43-1.174-2.5-2.5-2.5S16 13.07 16 14.5V20h-2V10Zm-2-6v2H4v5h8v2H4v5h8v2H2V4h10Z" />
            </symbol>
            <symbol viewBox="0 0 24 24" id="caseIcon">
                <path fill="currentColor"
                    d="m3.975 17l3.75-10h1.8l3.75 10H11.55l-.9-2.55H6.6L5.7 17H3.975Zm3.15-4h3l-1.45-4.15h-.1L7.125 13Zm9.225 4.275q-1.225 0-1.925-.638t-.7-1.737q0-1.05.813-1.713t2.087-.662q.575 0 1.063.088t.837.287v-.35q0-.675-.462-1.075t-1.263-.4q-.525 0-.988.225t-.787.65l-1.075-.8q.475-.675 1.2-1.025t1.675-.35q1.55 0 2.375.738t.825 2.137v4.4H18.55v-.85h-.075q-.325.5-.875.788t-1.25.287Zm.25-1.25q.8 0 1.363-.563t.562-1.362q-.35-.2-.8-.3t-.825-.1q-.8 0-1.225.313t-.425.887q0 .5.375.813t.975.312Z" />
            </symbol>
        </svg>
    </div>
    <div id="loadMask">
        <div>
            <div>ChatGPT</div>
            <div class="loadingCSSIcon">
                <div></div>
                <div></div>
                <div></div>
            </div>
        </div>
    </div>
    <div class="chat_window">
        <div class="overlay"></div>
        <nav class="nav">
            <div class="navHeader">
                <div id="newChat">
                    <svg width="24" height="24">
                        <use xlink:href="#addIcon" />
                    </svg>
                    <span data-i18n-key="newChat"></span>
                </div>
                <div id="newFolder" data-i18n-title="newFolder" title>
                    <svg width="24" height="24">
                        <use xlink:href="#newFolderIcon" />
                    </svg>
                </div>
            </div>
            <div class="extraChat">
                <input type="text" id="searchChat" autocomplete="off" readonly
                    onfocus="this.removeAttribute('readonly')" data-i18n-place="search" placeholder />
                <div id="clearSearch">
                    <svg width="24" height="24">
                        <use xlink:href="#closeIcon" />
                    </svg>
                </div>
                <div id="matchCaseSearch" data-i18n-title="matchCaseTip" title>
                    <svg width="24" height="24">
                        <use xlink:href="#caseIcon" />
                    </svg>
                </div>
            </div>
            <div class="allList">
                <div id="folderList"></div>
                <div id="chatList"></div>
            </div>
            <div class="navFooter">
                <div class="navFunc">
                    <div id="refreshPage" data-i18n-title="forceRe" title>
                        <svg width="24" height="24">
                            <use xlink:href="#forceRefreshIcon" />
                        </svg>
                    </div>
                    <div id="clearChat" data-i18n-title="clearAll" title>
                        <svg width="24" height="24">
                            <use xlink:href="#clearAllIcon" />
                        </svg>
                    </div>
                    <div id="toggleLight" data-i18n-theme title>
                        <svg width="24" height="24">
                            <use xlink:href="#lightThemeIcon" />
                        </svg>
                    </div>
                    <div id="sysSetting" data-i18n-title="setting" title>
                        <svg width="24" height="24">
                            <use xlink:href="#settingIcon" />
                        </svg>
                    </div>
                </div>
                <div class="divider"></div>
                <div class="links">
                    <a href="https://github.com/xqdoo00o/chatgpt-web" target="_blank"
                        rel="noopener noreferrer">Github</a>
                </div>
            </div>
        </nav>
        <div class="mainContent">
            <div class="top_menu">
                <div class="toggler" data-i18n-title="nav" title>
                    <div class="button close"></div>
                    <div class="button minimize"></div>
                    <div class="button maximize"></div>
                </div>
                <div class="title">
                    <div id="selector"><svg id="modelIcon" width="24" height="24">
                            <use xlink:href="#aiIcon"></use>
                        </svg><span id="modelVer">4o</span><svg width="16" height="16" style="margin-top: 1px">
                            <use xlink:href="#expandIcon" />
                        </svg>
                    </div>
                </div>
                <div class="settings">
                    <button class="setBtn" id="toggleFull" data-i18n-window title>
                        <svg width="30" height="30">
                            <use xlink:href="#expandFullIcon" />
                        </svg>
                    </button>
                    <button class="setBtn" id="setting">
                        <svg viewBox="0 0 100 100" width="30" height="30">
                            <title data-i18n-key="quickSet"></title>
                            <circle cx="50" cy="20" r="10" fill="#e15b64" />
                            <circle cx="50" cy="50" r="10" fill="#f8b26a" />
                            <circle cx="50" cy="80" r="10" fill="#99c959" />
                        </svg>
                    </button>
                </div>
                <div id="modelDialog" style="display:none;">
                    <div class="modelSingle" data-value="gpt-4o" data-type="1" data-ver="4o">
                        <div class="modelAvatar gptAvatar">
                            <svg width="24" height="24">
                                <use xlink:href="#aiIcon"></use>
                            </svg>
                        </div>
                        <div>GPT-4o</div>
                    </div>
                    <div class="modelSingle" data-value="gpt-4o-mini" data-type="1" data-ver="4o mini">
                        <div class="modelAvatar gptAvatar">
                            <svg width="24" height="24">
                                <use xlink:href="#aiIcon"></use>
                            </svg>
                        </div>
                        <div>GPT-4o-mini</div>
                    </div>
                    <div class="modelSingle" data-value="o1" data-type="1" data-ver="o1">
                        <div class="modelAvatar gptAvatar">
                            <svg width="24" height="24">
                                <use xlink:href="#aiIcon"></use>
                            </svg>
                        </div>
                        <div>o1</div>
                    </div>
                    <div class="modelSingle" data-value="o3" data-type="1" data-ver="o3 mini">
                        <div class="modelAvatar gptAvatar">
                            <svg width="24" height="24">
                                <use xlink:href="#aiIcon"></use>
                            </svg>
                        </div>
                        <div>o3-mini</div>
                    </div>
                    <div class="modelSingle hide" id="customGPTDOM" data-type="1" data-value="" data-ver="">
                        <div class="modelAvatar gptAvatar">
                            <svg width="24" height="24">
                                <use xlink:href="#aiIcon"></use>
                            </svg>
                        </div>
                        <div></div>
                    </div>
                    <div class="modelSingle" data-value="deepseek-chat" data-type="2" data-ver="Chat">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#deepseekIcon"></use>
                            </svg>
                        </div>
                        <div>DeepSeek Chat</div>
                    </div>
                    <div class="modelSingle" data-value="deepseek-reasoner" data-type="2" data-ver="Reasoner">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#deepseekIcon"></use>
                            </svg>
                        </div>
                        <div>DeepSeek Reasoner</div>
                    </div>
                    <div class="modelSingle hide" id="customDeepSeekDOM" data-type="2" data-value="" data-ver="">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#deepseekIcon"></use>
                            </svg>
                        </div>
                        <div></div>
                    </div>
                    <div class="modelSingle" data-value="azure|gpt-4o" data-type="3" data-ver="4o">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#azureAIIcon"></use>
                            </svg>
                        </div>
                        <div>Azure gpt-4o</div>
                    </div>
                    <div class="modelSingle hide" id="customAzureAIDOM" data-type="3" data-value="" data-ver="">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#azureAIIcon"></use>
                            </svg>
                        </div>
                        <div></div>
                    </div>
                    <div class="modelSingle" data-value="gemini-1.5-flash" data-type="4" data-ver="1.5 Flash">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#geminiIcon"></use>
                            </svg>
                        </div>
                        <div>Gemini 1.5 Flash</div>
                    </div>
                    <div class="modelSingle" data-value="gemini-1.5-pro" data-type="4" data-ver="1.5 Pro">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#geminiIcon"></use>
                            </svg>
                        </div>
                        <div>Gemini 1.5 Pro</div>
                    </div>
                    <div class="modelSingle" data-value="gemini-2.0-flash" data-type="4" data-ver="2.0 Flash">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#geminiIcon"></use>
                            </svg>
                        </div>
                        <div>Gemini 2.0 Flash</div>
                    </div>
                    <div class="modelSingle hide" id="customGeminiDOM" data-type="4" data-value="" data-ver="">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#geminiIcon"></use>
                            </svg>
                        </div>
                        <div></div>
                    </div>
                    <div class="modelSingle" data-value="claude-3-haiku-latest" data-type="5" data-ver="3 Haiku">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#claudeIcon"></use>
                            </svg>
                        </div>
                        <div>Claude 3 Haiku</div>
                    </div>
                    <div class="modelSingle" data-value="claude-3-sonnet-latest" data-type="5" data-ver="3 Sonnet">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#claudeIcon"></use>
                            </svg>
                        </div>
                        <div>Claude 3 Sonnet</div>
                    </div>
                    <div class="modelSingle" data-value="claude-3-opus-latest" data-type="5" data-ver="3 Opus">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#claudeIcon"></use>
                            </svg>
                        </div>
                        <div>Claude 3 Opus</div>
                    </div>
                    <div class="modelSingle hide" id="customClaudeDOM" data-type="5" data-value="" data-ver="">
                        <div class="modelAvatar">
                            <svg width="30" height="30">
                                <use xlink:href="#claudeIcon"></use>
                            </svg>
                        </div>
                        <div></div>
                    </div>
                </div>
                <div id="setDialog" style="display:none;">
                    <div class="setSwitch">
                        <div data-id="convOption" data-i18n-key="chat" class="activeSwitch"></div>
                        <div data-id="speechOption" data-i18n-key="tts"></div>
                        <div data-id="recOption" data-i18n-key="stt"></div>
                    </div>
                    <div id="convOption">
                        <div>
                            <div data-i18n-key="avatar"></div>
                            <div class="avatarDetail">
                                <img id="setAvatarPre" src="" />
                                <input class="inputTextClass" autocomplete="off" type="text" id="setAvatar" />
                            </div>
                        </div>
                        <div>
                            <div class="justSetLine presetSelect">
                                <div data-i18n-key="systemRole"></div>
                                <div>
                                    <label for="preSetSystem" data-i18n-key="presetRole"></label>
                                    <select id="preSetSystem">
                                        <option value="default" data-i18n-key="default"></option>
                                        <option value="normal" data-i18n-key="assistant"></option>
                                        <option value="cat" data-i18n-key="cat"></option>
                                        <option value="emoji" data-i18n-key="emoji"></option>
                                        <option value="image" data-i18n-key="withImg"></option>
                                    </select>
                                </div>
                            </div>
                            <textarea class="inputTextClass areaTextClass" autocomplete="off"
                                data-i18n-place="assistantText" placeholder id="systemInput"></textarea>
                        </div>
                        <div>
                            <span data-i18n-key="nature"></span>
                            <input type="range" id="top_p" min="0" max="1" value="1" step="0.05" />
                            <div class="selectDef">
                                <span data-i18n-key="natureNeg"></span>
                                <span data-i18n-key="naturePos"></span>
                            </div>
                        </div>
                        <div>
                            <span data-i18n-key="quality"></span>
                            <input type="range" id="temp" min="0" max="2" value="1" step="0.05" />
                            <div class="selectDef">
                                <span data-i18n-key="qualityNeg"></span>
                                <span data-i18n-key="qualityPos"></span>
                            </div>
                        </div>
                        <div>
                            <span data-i18n-key="chatsWidth"></span>
                            <input type="range" id="convWidth" min="30" max="100" value="100" step="1" />
                            <div class="selectDef">
                                <span>30%</span>
                                <span>100%</span>
                            </div>
                        </div>
                        <div>
                            <span data-i18n-key="typeSpeed"></span>
                            <input type="range" id="textSpeed" min="0" max="100" value="100" step="1" />
                            <div class="selectDef">
                                <span data-i18n-key="slow"></span>
                                <span data-i18n-key="fast"></span>
                            </div>
                        </div>
                        <div>
                            <span><span data-i18n-key="continuousLen"></span>: <span id="contLenWrap"></span><span
                                    data-i18n-key="msgAbbr"></span></span>
                            <input type="range" id="contLength" min="0" max="50" value="25" step="1" />
                            <div class="selectDef">
                                <span>0</span>
                                <span>50</span>
                            </div>
                        </div>
                        <div>
                            <span class="inlineTitle" data-i18n-key="COT"></span>
                            <label class="switch-slide">
                                <input type="checkbox" id="enableCOT" checked="true" hidden />
                                <label for="enableCOT" class="switch-slide-label"></label>
                            </label>
                        </div>
                        <div>
                            <span class="inlineTitle" data-i18n-key="longReply"></span>
                            <label class="switch-slide">
                                <input type="checkbox" id="enableLongReply" hidden />
                                <label for="enableLongReply" class="switch-slide-label"></label>
                            </label>
                        </div>
                    </div>
                    <div id="speechOption" style="display: none;">
                        <div class="presetSelect presetModelCls">
                            <label for="preSetService" data-i18n-key="ttsService"></label>
                            <select id="preSetService">
                                <option value="4" data-i18n-key="openaiTTS"></option>
                                <option value="3" data-i18n-key="azureTTS"></option>
                                <option selected value="2" data-i18n-key="edgeTTS"></option>
                                <option value="1" data-i18n-key="systemTTS"></option>
                            </select>
                        </div>
                        <div class="presetSelect presetModelCls">
                            <label for="preSetAzureRegion" data-i18n-key="azureRegion"></label>
                            <select id="preSetAzureRegion">
                            </select>
                        </div>
                        <div>
                            <div data-i18n-key="azureKey"></div>
                            <input class="inputTextClass" type="password" data-i18n-place="azureKey" id="azureKeyInput"
                                autocomplete="off" />
                        </div>
                        <div id="checkVoiceLoad" class="checkLoad" style="display: none;">
                            <svg>
                                <use xlink:href="#loadingIcon" />
                            </svg>
                            <span data-i18n-key="loadVoice"></span>
                        </div>
                        <div id="speechDetail">
                            <div>
                                <div class="justSetLine">
                                    <div data-i18n-key="voiceName" style="line-height: 28px;"></div>
                                    <div id="voiceTypes">
                                        <span data-type="0" data-i18n-key="userVoice"></span>
                                        <span data-type="1" class="selVoiceType" data-i18n-key="replyVoice"></span>
                                    </div>
                                </div>
                                <select id="preSetSpeech">
                                </select>
                            </div>
                            <div>
                                <div class="justSetLine">
                                    <input class="inputTextClass" id="testVoiceText" data-i18n-value="TTSTest" value />
                                </div>
                                <div class="justSetLine readyTestVoice" id="testVoiceBtn" style="margin-top: 6px;">
                                    <div class="justSetBtn" onclick="startTestVoice()">
                                        <svg width="18" height="18">
                                            <use xlink:href="#readyVoiceIcon" />
                                        </svg>
                                        <span data-i18n-key="play"></span>
                                    </div>
                                    <div class="justSetBtn" onclick="pauseTestVoice()">
                                        <svg width="18" height="18">
                                            <use xlink:href="#pauseVoiceIcon" />
                                        </svg>
                                        <span data-i18n-key="pause"></span>
                                    </div>
                                    <div class="justSetBtn" onclick="resumeTestVoice()">
                                        <svg width="18" height="18">
                                            <use xlink:href="#resumeVoiceIcon" />
                                        </svg>
                                        <span data-i18n-key="resume"></span>
                                    </div>
                                    <div class="justSetBtn" style="margin-right: 130px" onclick="stopTestVoice()">
                                        <svg width="18" height="18">
                                            <use xlink:href="#stopIcon" />
                                        </svg>
                                        <span data-i18n-key="stop"></span>
                                    </div>
                                </div>
                            </div>
                            <div class="justSetLine presetSelect" id="azureExtra" style="display:none;">
                                <div class="presetModelCls">
                                    <label for="preSetVoiceStyle" data-i18n-key="style"></label>
                                    <select id="preSetVoiceStyle">
                                    </select>
                                </div>
                                <div class="presetModelCls">
                                    <label for="preSetVoiceRole" data-i18n-key="role"></label>
                                    <select id="preSetVoiceRole">
                                    </select>
                                </div>
                            </div>
                            <div>
                                <span data-i18n-key="volume"></span>
                                <input type="range" id="voiceVolume" min="0.1" max="1.9" value="1" step="0.1" />
                                <div class="selectDef">
                                    <span data-i18n-key="low"></span>
                                    <span data-i18n-key="high"></span>
                                </div>
                            </div>
                            <div>
                                <span data-i18n-key="rate"></span>
                                <input type="range" id="voiceRate" min="0.1" max="1.9" value="1" step="0.1" />
                                <div class="selectDef">
                                    <span data-i18n-key="slow"></span>
                                    <span data-i18n-key="fast"></span>
                                </div>
                            </div>
                            <div>
                                <span data-i18n-key="pitch"></span>
                                <input type="range" id="voicePitch" min="0.1" max="1.9" value="1" step="0.1" />
                                <div class="selectDef">
                                    <span data-i18n-key="neutral"></span>
                                    <span data-i18n-key="intense"></span>
                                </div>
                            </div>
                            <div>
                                <span class="inlineTitle" data-i18n-key="contSpeech"></span>
                                <label class="switch-slide">
                                    <input type="checkbox" id="enableContVoice" checked="true" hidden />
                                    <label for="enableContVoice" class="switch-slide-label"></label>
                                </label>
                            </div>
                            <div>
                                <span class="inlineTitle" data-i18n-key="autoSpeech"></span>
                                <label class="switch-slide">
                                    <input type="checkbox" id="enableAutoVoice" hidden />
                                    <label for="enableAutoVoice" class="switch-slide-label"></label>
                                </label>
                            </div>
                        </div>
                    </div>
                    <div id="recOption" style="display: none;">
                        <div class="presetSelect presetModelCls">
                            <label for="preRecService" data-i18n-key="sttService"></label>
                            <select id="preRecService">
                                <option value="3" data-i18n-key="openaiTTS"></option>
                                <option value="2" data-i18n-key="azureTTS"></option>
                                <option value="1" data-i18n-key="systemTTS"></option>
                            </select>
                        </div>
                        <div>
                            <div class="presetSelect presetModelCls">
                                <label for="preRecAzureRegion" data-i18n-key="azureRegion"></label>
                                <select id="preRecAzureRegion">
                                </select>
                            </div>
                            <div data-i18n-key="azureKey"></div>
                            <input class="inputTextClass" type="password" data-i18n-place="azureKey"
                                id="azureRecKeyInput" autocomplete="off" />
                        </div>
                        <div id="checkRecLoad" class="checkLoad" style="display: none;">
                            <svg>
                                <use xlink:href="#loadingIcon" />
                            </svg>
                            <span data-i18n-key="loadRecVoice"></span>
                        </div>
                        <div id="noRecTip" style="display: none; margin-top: 15px" data-i18n-key="unsupportRecTip">
                        </div>
                        <div id="recDetail" style="display: none;">
                            <div class="presetSelect presetModelCls">
                                <label for="selectLangOption" data-i18n-key="lang"></label>
                                <select id="selectLangOption">
                                </select>
                            </div>
                            <div class="presetSelect presetModelCls">
                                <label for="selectDiaOption" data-i18n-key="dialect"></label>
                                <select id="selectDiaOption">
                                </select>
                            </div>
                            <div data-feat="forStream">
                                <div data-i18n-key="autoSendKey"></div>
                                <input class="inputTextClass" id="autoSendText" autocomplete="off"
                                    data-i18n-place="send" placeholder />
                            </div>
                            <div data-feat="forStream">
                                <div data-i18n-key="autoStopKey"></div>
                                <input class="inputTextClass" id="autoStopText" autocomplete="off"
                                    data-i18n-place="stop" placeholder />
                            </div>
                            <div data-feat="forStream">
                                <span data-i18n-key="autoSendDelay"></span>
                                <input type="range" id="autoSendTimeout" min="0" max="10" value="0" step="1" />
                                <div class="selectDef">
                                    <span>0<span data-i18n-key="second"></span></span>
                                    <span>10<span data-i18n-key="second"></span></span>
                                </div>
                            </div>
                            <div data-i18n-key="noStreamTip" data-feat="forNoStream"></div>
                            <div>
                                <span class="inlineTitle" data-i18n-key="keepListenMic"></span>
                                <label class="switch-slide">
                                    <input type="checkbox" id="keepListenMic" checked="false" hidden />
                                    <label for="keepListenMic" class="switch-slide-label"></label>
                                </label>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="messages">
                <div id="chatlog"></div>
                <div id="stopChat"><svg width="24" height="24">
                        <use xlink:href="#stopResIcon" />
                    </svg><span data-i18n-key="stop"></span></div>
            </div>
            <div class="bottom_wrapper clearfix">
                <div class="message_input_wrapper">
                    <textarea class="message_input_text" autocomplete="off" spellcheck="false" data-i18n-place="askTip"
                        placeholder id="chatinput"></textarea>
                    <div id="voiceRec" style="display:none;">
                        <div id="voiceRecIcon">
                            <svg viewBox="0 0 48 48" id="voiceInputIcon">
                                <g fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="4">
                                    <rect fill="none" width="14" height="27" x="17" y="4" rx="7" />
                                    <rect class="animVoice" x="18" y="4" width="12" height="27" stroke="none"
                                        fill="currentColor"></rect>
                                    <path stroke-linecap="round"
                                        d="M9 23c0 8.284 6.716 15 15 15c8.284 0 15-6.716 15-15M24 38v6" />
                                </g>
                            </svg>
                        </div>
                        <div id="voiceRecSetting">
                            <select id="select_language" style="margin-bottom: 4px;"></select>
                            <select id="select_dialect"></select>
                        </div>
                    </div>
                </div>
                <button class="loaded" id="sendbutton">
                    <span data-i18n-key="send"></span>
                    <svg style="margin:0 auto;height:40px;width:100%;">
                        <use xlink:href="#loadingIcon" />
                    </svg>
                </button>
                <button class="clearConv" data-i18n-title="clearChat" title>
                    <svg style="color: #e15b64;" width="29" height="29">
                        <use xlink:href="#closeIcon" />
                    </svg>
                    <svg width="21" height="21">
                        <use xlink:href="#deleteIcon" />
                    </svg>
                </button>
            </div>
        </div>
    </div>
    <div id="sysMask">
        <div id="sysDialog">
            <div id="closeSet">
                <svg width="24" height="24">
                    <use xlink:href="#closeIcon" />
                </svg>
            </div>
            <div class="sysTitle" data-i18n-key="setting"></div>
            <div class="sysContent">
                <div class="sysSwitch">
                    <div data-id="modelsOption" class="activeSwitch">
                        <svg width="24" height="24">
                            <use xlink:href="#modelsIcon" />
                        </svg><span data-i18n-key="models"></span>
                    </div>
                    <div data-id="uiOption">
                        <svg width="24" height="24">
                            <use xlink:href="#uiIcon" />
                        </svg><span data-i18n-key="ui"></span>
                    </div>
                    <div data-id="hotkeyOption">
                        <svg width="24" height="24">
                            <use xlink:href="#hotkeyIcon" />
                        </svg><span data-i18n-key="hotkey"></span>
                    </div>
                    <div data-id="dataOption">
                        <svg width="24" height="24">
                            <use xlink:href="#databaseIcon" />
                        </svg><span data-i18n-key="data"></span>
                    </div>
                </div>
                <div class="sysDetail">
                    <div id="modelsOption">
                        <div class="modelSwitch">
                            <div data-id="gptOption" class="activeSwitch">ChatGPT</div>
                            <div data-id="deepseekOption">DeepSeek</div>
                            <div data-id="azureOption">Azure</div>
                            <div data-id="geminiOption">Gemini</div>
                            <div data-id="claudeOption">Claude</div>
                        </div>
                        <div class="apiSelsContainer">
                            <div id="apiSelect" tabindex="-1" style="display: none;"></div>
                        </div>
                        <div id="gptOption">
                            <div class="setContent setNotNormalFlow">
                                <div class="setTitle"><span>ChatGPT</span><span data-i18n-key="aiEndpoint"></span></div>
                                <div class="setDetail inputDetail" style="position: relative;">
                                    <input class="inputTextClass" placeholder="https://api.openai.com/"
                                        autocomplete="off" id="apiHostInput" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle"><span data-i18n-key="aiKey"></span></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="password" placeholder="sk-xxxxxx" id="keyInput"
                                        autocomplete="off" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiModel"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="text" placeholder="gpt-x-xxx" id="modelInput"
                                        autocomplete="off" />
                                </div>
                            </div>
                        </div>
                        <div id="deepseekOption" style="display: none;">
                            <div class="setContent setNotNormalFlow">
                                <div class="setTitle"><span>DeepSeek</span><span data-i18n-key="aiEndpoint"></span>
                                </div>
                                <div class="setDetail inputDetail" style="position: relative;">
                                    <input class="inputTextClass" placeholder="https://api.deepseek.com/"
                                        autocomplete="off" id="deepseekApiHostInput" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiKey"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="password" placeholder="sk-xxxxxx"
                                        id="deepseekKeyInput" autocomplete="off" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiModel"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="text" placeholder="deepseek-xxx"
                                        id="deepseekModelInput" autocomplete="off" />
                                </div>
                            </div>
                        </div>
                        <div id="azureOption" style="display: none;">
                            <div class="setContent setNotNormalFlow">
                                <div class="setTitle"><span>Azure</span><span data-i18n-key="aiEndpoint"></span></div>
                                <div class="setDetail inputDetail" style="position: relative;">
                                    <input class="inputTextClass"
                                        placeholder="https://ai-xx.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview"
                                        autocomplete="off" id="azureAIApiHostInput" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiKey"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="password" placeholder="xxxxxxxx"
                                        id="azureAIKeyInput" autocomplete="off" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiModel"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="text" placeholder="xxxx-xx"
                                        id="azureAIModelInput" autocomplete="off" />
                                </div>
                            </div>
                        </div>
                        <div id="geminiOption" style="display: none;">
                            <div class="setContent setNotNormalFlow">
                                <div class="setTitle"><span>Gemini</span><span data-i18n-key="aiEndpoint"></span></div>
                                <div class="setDetail inputDetail" style="position: relative;">
                                    <input class="inputTextClass"
                                        placeholder="https://generativelanguage.googleapis.com/" autocomplete="off"
                                        id="geminiApiHostInput" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiKey"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="password" placeholder="xxxx-xxxx"
                                        id="geminiKeyInput" autocomplete="off" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiModel"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="text" placeholder="gemini-xxx-xxx"
                                        id="geminiModelInput" autocomplete="off" />
                                </div>
                            </div>
                        </div>
                        <div id="claudeOption" style="display: none;">
                            <div class="setContent setNotNormalFlow">
                                <div class="setTitle"><span>Claude</span><span data-i18n-key="aiEndpoint"></span></div>
                                <div class="setDetail inputDetail" style="position: relative;">
                                    <input class="inputTextClass" placeholder="https://api.anthropic.com/"
                                        autocomplete="off" id="claudeApiHostInput" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiKey"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="password" placeholder="sk-xxxxxx"
                                        id="claudeKeyInput" autocomplete="off" />
                                </div>
                            </div>
                            <div class="setContent">
                                <div class="setTitle" data-i18n-key="aiModel"></div>
                                <div class="setDetail inputDetail">
                                    <input class="inputTextClass" type="text" placeholder="claude-x-xxx-xxxx"
                                        id="claudeModelInput" autocomplete="off" />
                                </div>
                            </div>
                        </div>
                    </div>
                    <div id="uiOption" style="display: none;">
                        <div class="setContent">
                            <div class="setTitle" data-i18n-key="theme"></div>
                            <div class="setDetail themeDetail lightTheme" id="setLight">
                                <div data-i18n-title="darkTheme" title>
                                    <svg width="24" height="24">
                                        <use xlink:href="#darkThemeIcon"></use>
                                    </svg>
                                </div>
                                <div data-i18n-title="lightTheme" title>
                                    <svg width="24" height="24">
                                        <use xlink:href="#lightThemeIcon"></use>
                                    </svg>
                                </div>
                                <div data-i18n-title="autoWord" title>
                                    <svg width="24" height="24">
                                        <use xlink:href="#autoThemeIcon"></use>
                                    </svg>
                                </div>
                            </div>
                        </div>
                        <div class="setContent" id="autoDetail" style="display: none;font-size: 15px;">
                            <div class="setDetail">
                                <div class="autoSelect">
                                    <input type="radio" id="autoTheme1" name="autoLight" value="1" checked />
                                    <label for="autoTheme1" data-i18n-key="systemTheme"></label>
                                </div>
                                <div class="autoSelect" style="margin-top: 8px;">
                                    <input type="radio" id="autoTheme0" name="autoLight" value="0" />
                                    <label for="autoTheme0" data-i18n-key="customDarkTheme"></label>
                                </div>
                                <div id="customAutoSet" style="display: none; margin-top: 10px;">
                                    <div>
                                        <label for="customStart" data-i18n-key="startDark"></label>
                                        <input type="time" id="customStart" required>
                                    </div>
                                    <div style="margin-top: 10px;">
                                        <label for="customEnd" data-i18n-key="endDark"></label>
                                        <input type="time" id="customEnd" required>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="setContent">
                            <div class="setTitle" data-i18n-key="lang"></div>
                            <div class="setDetail themeDetail langDetail" id="setLang">
                                <div title="English">
                                    <svg width="24" height="24">
                                        <use xlink:href="#enIcon"></use>
                                    </svg>
                                </div>
                                <div title="中文">
                                    <svg width="24" height="24">
                                        <use xlink:href="#zhIcon"></use>
                                    </svg>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div id="hotkeyOption" style="display: none;">
                        <div class="setContent">
                            <div class="setTitle">UI</div>
                            <div class="setDetail hotKeyDetail">
                                <div>
                                    <label for="hotKeyNav" data-i18n-key="navKey"></label>
                                    <select id="hotKeyNav">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyWindow" data-i18n-key="fullKey"></label>
                                    <select id="hotKeyWindow">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyTheme" data-i18n-key="themeKey"></label>
                                    <select id="hotKeyTheme">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyLang" data-i18n-key="langKey"></label>
                                    <select id="hotKeyLang">
                                    </select>
                                </div>
                            </div>
                            <div class="setTitle" data-i18n-key="chat"></div>
                            <div class="setDetail hotKeyDetail">
                                <div>
                                    <label for="hotKeySearch" data-i18n-key="search"></label>
                                    <select id="hotKeySearch">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyInput" data-i18n-key="inputKey"></label>
                                    <select id="hotKeyInput">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyNewChat" data-i18n-key="newChat"></label>
                                    <select id="hotKeyNewChat">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyClearChat" data-i18n-key="clearChat"></label>
                                    <select id="hotKeyClearChat">
                                    </select>
                                </div>
                            </div>
                            <div class="setTitle" data-i18n-key="voiceKey"></div>
                            <div class="setDetail hotKeyDetail">
                                <div style="display: none;">
                                    <label for="hotKeyVoiceRec" data-i18n-key="recKey"></label>
                                    <select id="hotKeyVoiceRec">
                                    </select>
                                </div>
                                <div>
                                    <label for="hotKeyVoiceSpeak" data-i18n-key="speechKey"></label>
                                    <select id="hotKeyVoiceSpeak">
                                    </select>
                                </div>
                            </div>
                            <div class="setDetail keyOptionDetail">
                                <div id="resetHotKey">
                                    <svg width="24" height="24" style="transform: scaleX(-1)">
                                        <use xlink:href="#refreshIcon" />
                                    </svg>
                                    <span data-i18n-key="resetTip"></span>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div id="dataOption" style="display: none;">
                        <div class="setContent">
                            <div class="setTitle" data-i18n-key="chat"></div>
                            <div class="setDetail dataDetail">
                                <div id="exportChat">
                                    <svg width="24" height="24">
                                        <use xlink:href="#exportIcon" />
                                    </svg>
                                    <span data-i18n-key="export"></span>
                                </div>
                                <label id="importChat" for="importChatInput">
                                    <svg width="24" height="24">
                                        <use xlink:href="#importIcon" />
                                    </svg>
                                    <span data-i18n-key="import"></span>
                                </label>
                                <input type="file" style="display: none;" id="importChatInput"
                                    accept="application/json" />
                                <div id="clearChatSet">
                                    <svg width="24" height="24">
                                        <use xlink:href="#clearAllIcon" />
                                    </svg>
                                    <span data-i18n-key="clear"></span>
                                </div>
                            </div>
                        </div>
                        <div class="setContent">
                            <div class="setTitle" data-i18n-key="setting"></div>
                            <div class="setDetail dataDetail">
                                <div id="exportSet">
                                    <svg width="24" height="24">
                                        <use xlink:href="#exportSetIcon" />
                                    </svg>
                                    <span data-i18n-key="export"></span>
                                </div>
                                <label id="importSet" for="importSetInput">
                                    <svg width="24" height="24">
                                        <use xlink:href="#importSetIcon" />
                                    </svg>
                                    <span data-i18n-key="import"></span>
                                </label>
                                <input type="file" style="display: none;" id="importSetInput"
                                    accept="application/json" />
                                <div id="resetSet">
                                    <svg width="24" height="24" style="transform: scaleX(-1)">
                                        <use xlink:href="#refreshIcon" />
                                    </svg>
                                    <span data-i18n-key="reset"></span>
                                </div>
                            </div>
                        </div>
                        <div class="setContent">
                            <div class="setTitle" data-i18n-key="localStore"></div>
                            <div class="setDetail">
                                <div class="progressBar">
                                    <div class="nowProgress" id="usedStorageBar"></div>
                                </div>
                                <div class="progressDetail">
                                    <div><span data-i18n-key="used"></span><span id="usedStorage"></span></div>
                                    <div><span data-i18n-key="available"></span><span id="availableStorage"></span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <link crossorigin="anonymous"
        href="https://fastly.jsdelivr.net/npm/github-markdown-css@5.8.1/github-markdown-light.min.css" rel="stylesheet">
    <link crossorigin="anonymous"
        href="https://fastly.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/github.min.css"
        rel="stylesheet">
    <link crossorigin="anonymous" href="https://fastly.jsdelivr.net/npm/notyf@3.10.0/notyf.min.css" rel="stylesheet">
    <script crossorigin="anonymous" src="https://fastly.jsdelivr.net/npm/notyf@3.10.0/notyf.min.js"></script>
    <script>
        const notyf = new Notyf({
            position: { x: "center", y: "top" },
            types: [
                {
                    type: "success",
                    background: "#99c959",
                    duration: 2000,
                },
                {
                    type: "warning",
                    background: "#f8b26a",
                    duration: 3000
                },
                {
                    type: "error",
                    background: "#e15b64",
                    duration: 3000,
                }
            ]
        });
        const registerSW = () => {
            if ("serviceWorker" in navigator) {
                navigator.serviceWorker.register("sw.js" + location.search).then(reg => console.log("Service worker register succeeded"),
                    error => console.error(`Service worker register failed: ${error}`))
            }
        };
        window.addEventListener("load", () => registerSW());
        const isMobile = navigator.userAgent.match(/iPhone|iPad|iPod|Android|BlackBerry|webOS/);
        if (isMobile) {
            const script = document.createElement("script");
            script.src = "https://fastly.jsdelivr.net/gh/timruffles/mobile-drag-drop@3.0.0-rc.0/release/index.min.js";
            script.crossOrigin = "anonymous";
            script.defer = true;
            script.onload = () => {
                MobileDragDrop.polyfill();
            }
            document.body.appendChild(script);
            const link = document.createElement("link");
            link.crossOrigin = "anonymous";
            link.rel = "stylesheet";
            link.href = "https://fastly.jsdelivr.net/gh/timruffles/mobile-drag-drop@3.0.0-rc.0/release/default.css";
            document.body.appendChild(link);
        }
        let envAPIEndpoint, envAPIKey, envAPIModel, envDeepSeekAPIEndpoint, envDeepSeekAPIKey, envDeepSeekAPIModel, envAzureAIAPIEndpoint, envAzureAIAPIKey, envAzureAIAPIModel, envGeminiAPIEndpoint, envGeminiAPIKey, envGeminiAPIModel, envClaudeAPIEndpoint, envClaudeAPIKey, envClaudeAPIModel;
    </script>
    <script src="env.js"></script>
    <script>
        // from fflate@0.8.2
        {const t=Uint8Array,e=Uint16Array,n=Int32Array,r=new t([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),o=new t([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),l=new t([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),s=(t,r)=>{const o=new e(31);for(let e=0;e<31;++e)o[e]=r+=1<<t[e-1];const l=new n(o[30]);for(let t=1;t<30;++t)for(let e=o[t];e<o[t+1];++e)l[e]=e-o[t]<<5|t;return{b:o,r:l}},{b:f,r:c}=s(r,2);f[28]=258,c[258]=28;const{b:i,r:a}=s(o,0),h=new e(32768);for(let t=0;t<32768;++t){let e=(43690&t)>>1|(21845&t)<<1;e=(52428&e)>>2|(13107&e)<<2,e=(61680&e)>>4|(3855&e)<<4,h[t]=((65280&e)>>8|(255&e)<<8)>>1}const u=(t,n,r)=>{const o=t.length;let l=0;const s=new e(n);for(;l<o;++l)t[l]&&++s[t[l]-1];const f=new e(n);for(l=1;l<n;++l)f[l]=f[l-1]+s[l-1]<<1;let c;if(r){c=new e(1<<n);const r=15-n;for(l=0;l<o;++l)if(t[l]){const e=l<<4|t[l],o=n-t[l];let s=f[t[l]-1]++<<o;for(const t=s|(1<<o)-1;s<=t;++s)c[h[s]>>r]=e}}else for(c=new e(o),l=0;l<o;++l)t[l]&&(c[l]=h[f[t[l]-1]++]>>15-t[l]);return c},w=new t(288);for(let t=0;t<144;++t)w[t]=8;for(let t=144;t<256;++t)w[t]=9;for(let t=256;t<280;++t)w[t]=7;for(let t=280;t<288;++t)w[t]=8;const g=new t(32);for(let t=0;t<32;++t)g[t]=5;const b=u(w,9,0),d=u(w,9,1),m=u(g,5,0),y=u(g,5,1),M=t=>{let e=t[0];for(let n=1;n<t.length;++n)t[n]>e&&(e=t[n]);return e},p=(t,e,n)=>{const r=e/8|0;return(t[r]|t[r+1]<<8)>>(7&e)&n},k=(t,e)=>{const n=e/8|0;return(t[n]|t[n+1]<<8|t[n+2]<<16)>>(7&e)},v=t=>(t+7)/8|0,x=(e,n,r)=>((null==n||n<0)&&(n=0),(null==r||r>e.length)&&(r=e.length),new t(e.subarray(n,r))),E=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],A=(t,e,n)=>{const r=new Error(e||E[t]);if(r.code=t,Error.captureStackTrace&&Error.captureStackTrace(r,A),!n)throw r;return r},T=(e,n,s,c)=>{const a=e.length,h=c?c.length:0;if(!a||n.f&&!n.l)return s||new t(0);const w=!s,g=w||2!=n.i,b=n.i;w&&(s=new t(3*a));const m=e=>{let n=s.length;if(e>n){const r=new t(Math.max(2*n,e));r.set(s),s=r}};let E=n.f||0,T=n.p||0,U=n.b||0,z=n.l,F=n.d,S=n.m,I=n.n;const O=8*a;do{if(!z){E=p(e,T,1);const r=p(e,T+1,3);if(T+=3,!r){const t=v(T)+4,r=e[t-4]|e[t-3]<<8,o=t+r;if(o>a){b&&A(0);break}g&&m(U+r),s.set(e.subarray(t,o),U),n.b=U+=r,n.p=T=8*o,n.f=E;continue}if(1==r)z=d,F=y,S=9,I=5;else if(2==r){const n=p(e,T,31)+257,r=p(e,T+10,15)+4,o=n+p(e,T+5,31)+1;T+=14;const s=new t(o),f=new t(19);for(let t=0;t<r;++t)f[l[t]]=p(e,T+3*t,7);T+=3*r;const c=M(f),i=(1<<c)-1,a=u(f,c,1);for(let t=0;t<o;){const n=a[p(e,T,i)];T+=15&n;const r=n>>4;if(r<16)s[t++]=r;else{let n=0,o=0;for(16==r?(o=3+p(e,T,3),T+=2,n=s[t-1]):17==r?(o=3+p(e,T,7),T+=3):18==r&&(o=11+p(e,T,127),T+=7);o--;)s[t++]=n}}const h=s.subarray(0,n),w=s.subarray(n);S=M(h),I=M(w),z=u(h,S,1),F=u(w,I,1)}else A(1);if(T>O){b&&A(0);break}}g&&m(U+131072);const w=(1<<S)-1,x=(1<<I)-1;let j=T;for(;;j=T){const t=z[k(e,T)&w],n=t>>4;if(T+=15&t,T>O){b&&A(0);break}if(t||A(2),n<256)s[U++]=n;else{if(256==n){j=T,z=null;break}{let t=n-254;if(n>264){const o=n-257,l=r[o];t=p(e,T,(1<<l)-1)+f[o],T+=l}const l=F[k(e,T)&x],a=l>>4;l||A(3),T+=15&l;let u=i[a];if(a>3){const t=o[a];u+=k(e,T)&(1<<t)-1,T+=t}if(T>O){b&&A(0);break}g&&m(U+131072);const w=U+t;if(U<u){const t=h-u,e=Math.min(u,w);for(t+U<0&&A(3);U<e;++U)s[U]=c[t+U]}for(;U<w;++U)s[U]=s[U-u]}}}n.l=z,n.p=j,n.b=U,n.f=E,z&&(E=1,n.m=S,n.d=F,n.n=I)}while(!E);return U!=s.length&&w?x(s,0,U):s.subarray(0,U)},U=(t,e,n)=>{n<<=7&e;const r=e/8|0;t[r]|=n,t[r+1]|=n>>8},z=(t,e,n)=>{n<<=7&e;const r=e/8|0;t[r]|=n,t[r+1]|=n>>8,t[r+2]|=n>>16},F=(n,r)=>{const o=[];for(let t=0;t<n.length;++t)n[t]&&o.push({s:t,f:n[t]});const l=o.length,s=o.slice();if(!l)return{t:C,l:0};if(1==l){const e=new t(o[0].s+1);return e[o[0].s]=1,{t:e,l:1}}o.sort(((t,e)=>t.f-e.f)),o.push({s:-1,f:25001});let f=o[0],c=o[1],i=0,a=1,h=2;for(o[0]={s:-1,f:f.f+c.f,l:f,r:c};a!=l-1;)f=o[o[i].f<o[h].f?i++:h++],c=o[i!=a&&o[i].f<o[h].f?i++:h++],o[a++]={s:-1,f:f.f+c.f,l:f,r:c};let u=s[0].s;for(let t=1;t<l;++t)s[t].s>u&&(u=s[t].s);const w=new e(u+1);let g=S(o[a-1],w,0);if(g>r){let t=0,e=0;const n=g-r,o=1<<n;for(s.sort(((t,e)=>w[e.s]-w[t.s]||t.f-e.f));t<l;++t){const n=s[t].s;if(!(w[n]>r))break;e+=o-(1<<g-w[n]),w[n]=r}for(e>>=n;e>0;){const n=s[t].s;w[n]<r?e-=1<<r-w[n]++-1:++t}for(;t>=0&&e;--t){const n=s[t].s;w[n]==r&&(--w[n],++e)}g=r}return{t:new t(w),l:g}},S=(t,e,n)=>-1==t.s?Math.max(S(t.l,e,n+1),S(t.r,e,n+1)):e[t.s]=n,I=t=>{let n=t.length;for(;n&&!t[--n];);const r=new e(++n);let o=0,l=t[0],s=1;const f=t=>{r[o++]=t};for(let e=1;e<=n;++e)if(t[e]==l&&e!=n)++s;else{if(!l&&s>2){for(;s>138;s-=138)f(32754);s>2&&(f(s>10?s-11<<5|28690:s-3<<5|12305),s=0)}else if(s>3){for(f(l),--s;s>6;s-=6)f(8304);s>2&&(f(s-3<<5|8208),s=0)}for(;s--;)f(l);s=1,l=t[e]}return{c:r.subarray(0,o),n:n}},O=(t,e)=>{let n=0;for(let r=0;r<e.length;++r)n+=t[r]*e[r];return n},j=(t,e,n)=>{const r=n.length,o=v(e+2);t[o]=255&r,t[o+1]=r>>8,t[o+2]=255^t[o],t[o+3]=255^t[o+1];for(let e=0;e<r;++e)t[o+e+4]=n[e];return 8*(o+4+r)},q=(t,n,s,f,c,i,a,h,d,y,M)=>{U(n,M++,s),++c[256];const{t:p,l:k}=F(c,15),{t:v,l:x}=F(i,15),{c:E,n:A}=I(p),{c:T,n:S}=I(v),q=new e(19);for(let t=0;t<E.length;++t)++q[31&E[t]];for(let t=0;t<T.length;++t)++q[31&T[t]];const{t:B,l:C}=F(q,7);let D=19;for(;D>4&&!B[l[D-1]];--D);const G=y+5<<3,H=O(c,w)+O(i,g)+a,J=O(c,p)+O(i,v)+a+14+3*D+O(q,B)+2*q[16]+3*q[17]+7*q[18];if(d>=0&&G<=H&&G<=J)return j(n,M,t.subarray(d,d+y));let K,L,N,P;if(U(n,M,1+(J<H)),M+=2,J<H){K=u(p,k,0),L=p,N=u(v,x,0),P=v;const t=u(B,C,0);U(n,M,A-257),U(n,M+5,S-1),U(n,M+10,D-4),M+=14;for(let t=0;t<D;++t)U(n,M+3*t,B[l[t]]);M+=3*D;const e=[E,T];for(let r=0;r<2;++r){const o=e[r];for(let e=0;e<o.length;++e){const r=31&o[e];U(n,M,t[r]),M+=B[r],r>15&&(U(n,M,o[e]>>5&127),M+=o[e]>>12)}}}else K=b,L=w,N=m,P=g;for(let t=0;t<h;++t){const e=f[t];if(e>255){const t=e>>18&31;z(n,M,K[t+257]),M+=L[t+257],t>7&&(U(n,M,e>>23&31),M+=r[t]);const l=31&e;z(n,M,N[l]),M+=P[l],l>3&&(z(n,M,e>>5&8191),M+=o[l])}else z(n,M,K[e]),M+=L[e]}return z(n,M,K[256]),M+L[256]},B=new n([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),C=new t(0),D=(l,s,f,i,h,u)=>{const w=u.z||l.length,g=new t(i+w+5*(1+Math.ceil(w/7e3))+h),b=g.subarray(i,g.length-h),d=u.l;let m=7&(u.r||0);if(s){m&&(b[0]=u.r>>3);const t=B[s-1],i=t>>13,h=8191&t,g=(1<<f)-1,y=u.p||new e(32768),M=u.h||new e(g+1),p=Math.ceil(f/3),k=2*p,v=t=>(l[t]^l[t+1]<<p^l[t+2]<<k)&g,x=new n(25e3),E=new e(288),A=new e(32);let T=0,U=0,z=u.i||0,F=0,S=u.w||0,I=0;for(;z+2<w;++z){const t=v(z);let e=32767&z,n=M[t];if(y[e]=n,M[t]=e,S<=z){const s=w-z;if((T>7e3||F>24576)&&(s>423||!d)){m=q(l,b,0,x,E,A,U,F,I,z-I,m),F=T=U=0,I=z;for(let t=0;t<286;++t)E[t]=0;for(let t=0;t<30;++t)A[t]=0}let f=2,u=0,g=h,M=e-n&32767;if(s>2&&t==v(z-M)){const t=Math.min(i,s)-1,r=Math.min(32767,z),o=Math.min(258,s);for(;M<=r&&--g&&e!=n;){if(l[z+f]==l[z+f-M]){let e=0;for(;e<o&&l[z+e]==l[z+e-M];++e);if(e>f){if(f=e,u=M,e>t)break;const r=Math.min(M,e-2);let o=0;for(let t=0;t<r;++t){const e=z-M+t&32767,r=e-y[e]&32767;r>o&&(o=r,n=e)}}}e=n,n=y[e],M+=e-n&32767}}if(u){x[F++]=268435456|c[f]<<18|a[u];const t=31&c[f],e=31&a[u];U+=r[t]+o[e],++E[257+t],++A[e],S=z+f,++T}else x[F++]=l[z],++E[l[z]]}}for(z=Math.max(z,S);z<w;++z)x[F++]=l[z],++E[l[z]];m=q(l,b,d,x,E,A,U,F,I,z-I,m),d||(u.r=7&m|b[m/8|0]<<3,m-=7,u.h=M,u.p=y,u.i=z,u.w=S)}else{for(let t=u.w||0;t<w+d;t+=65535){let e=t+65535;e>=w&&(b[m/8|0]=d,e=w),m=j(b,m+1,l.subarray(t,e))}u.i=w}return x(g,0,i+v(m)+h)},G=(e,n,r,o,l)=>{if(!l&&(l={l:1},n.dictionary)){const r=n.dictionary.subarray(-32768),o=new t(r.length+e.length);o.set(r),o.set(e,r.length),e=o,l.w=r.length}return D(e,null==n.level?6:n.level,null==n.mem?l.l?Math.ceil(1.5*Math.max(8,Math.min(13,Math.log(e.length)))):20:12+n.mem,r,o,l)};function deflateSync(t,e){return G(t,e||{},0,0)}function inflateSync(t,e){return T(t,{i:2},e&&e.out,e&&e.dictionary)}};
        // from markdown-it-texmath@1.0.1
        function escapeHTML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;").replace(/\//g,"&sol;")}function texmath(e,t){const n=texmath.mergeDelimiters(t&&t.delimiters),a=t&&t.outerSpace||!1,o=t&&t.katexOptions||{};o.throwOnError=o.throwOnError||!1,o.macros=o.macros||t&&t.macros,texmath.katex||(t&&"object"==typeof t.engine?texmath.katex=t.engine:"object"==typeof module?texmath.katex=require("katex"):texmath.katex={renderToString:()=>"No math renderer found."});for(const t of n.inline)a&&"outerSpace"in t&&(t.outerSpace=!0),e.inline.ruler.before("escape",t.name,texmath.inline(t)),e.renderer.rules[t.name]=(e,n)=>t.tmpl.replace(/\$1/,texmath.render(e[n].content,!!t.displayMode,o));for(const t of n.block)e.block.ruler.before("fence",t.name,texmath.block(t)),e.renderer.rules[t.name]=(e,n)=>t.tmpl.replace(/\$2/,escapeHTML(e[n].info)).replace(/\$1/,texmath.render(e[n].content,!0,o))}texmath.mergeDelimiters=function(e){const t=Array.isArray(e)?e:"string"==typeof e?[e]:["dollars"],n={inline:[],block:[]};for(const e of t)e in texmath.rules&&(n.inline.push(...texmath.rules[e].inline),n.block.push(...texmath.rules[e].block));return n},texmath.inline=e=>function(t,n){const a=t.pos,o=t.src,r=o.startsWith(e.tag,e.rex.lastIndex=a)&&(!e.pre||e.pre(o,e.outerSpace,a))&&e.rex.exec(o),s=!!r&&a<e.rex.lastIndex&&(!e.post||e.post(o,e.outerSpace,e.rex.lastIndex-1));if(s){if(!n){const n=t.push(e.name,"math",0);n.content=r[1],n.markup=e.tag}t.pos=e.rex.lastIndex}return s},texmath.block=e=>function(t,n,a,o){const r=t.bMarks[n]+t.tShift[n],s=t.src,m=s.startsWith(e.tag,e.rex.lastIndex=r)&&(!e.pre||e.pre(s,!1,r))&&e.rex.exec(s),l=!!m&&r<e.rex.lastIndex&&(!e.post||e.post(s,!1,e.rex.lastIndex-1));if(l&&!o){const o=e.rex.lastIndex-1;let r;for(r=n;r<a&&!(o>=t.bMarks[r]+t.tShift[r]&&o<=t.eMarks[r]);r++);const s=t.lineMax,l=t.parentType;t.lineMax=r,t.parentType="math","blockquote"===l&&(m[1]=m[1].replace(/(\n*?^(?:\s*>)+)/gm,""));let c=t.push(e.name,"math",0);c.block=!0,c.tag=e.tag,c.markup="",c.content=m[1],c.info=m[m.length-1],c.map=[n,r+1],t.parentType=l,t.lineMax=s,t.line=r+1}return l},texmath.render=function(e,t,n){let a;n.displayMode=t;try{a=texmath.katex.renderToString(e,n)}catch(t){a=escapeHTML(`${e}:${t.message}`)}return a},texmath.inlineRuleNames=["math_inline","math_inline_double"],texmath.blockRuleNames=["math_block","math_block_eqno"],texmath.$_pre=(e,t,n)=>{const a=n>0&&e[n-1].charCodeAt(0);return t?!a||32===a:!a||92!==a&&(a<48||a>57)},texmath.$_post=(e,t,n)=>{const a=e[n+1]&&e[n+1].charCodeAt(0);return t?!a||32===a||46===a||44===a||59===a:!a||a<48||a>57},texmath.rules={brackets:{inline:[{name:"math_inline",rex:/\\\((.+?)\\\)/gy,tmpl:"<eq>$1</eq>",tag:"\\("}],block:[{name:"math_block_eqno",rex:/\\\[(((?!\\\]|\\\[)[\s\S])+?)\\\]\s*?\(([^)$\r\n]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"\\["},{name:"math_block",rex:/\\\[([\s\S]+?)\\\]/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"\\["}]},doxygen:{inline:[{name:"math_inline",rex:/\\f\$(.+?)\\f\$/gy,tmpl:"<eq>$1</eq>",tag:"\\f$"}],block:[{name:"math_block_eqno",rex:/\\f\[([^]+?)\\f\]\s*?\(([^)\s]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"\\f["},{name:"math_block",rex:/\\f\[([^]+?)\\f\]/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"\\f["}]},gitlab:{inline:[{name:"math_inline",rex:/\$`(.+?)`\$/gy,tmpl:"<eq>$1</eq>",tag:"$`"}],block:[{name:"math_block_eqno",rex:/`{3}math\s*([^`]+?)\s*?`{3}\s*\(([^)\r\n]+?)\)/gm,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"```math"},{name:"math_block",rex:/`{3}math\s*([^`]*?)\s*`{3}/gm,tmpl:"<section><eqn>$1</eqn></section>",tag:"```math"}]},julia:{inline:[{name:"math_inline",rex:/`{2}([^`]+?)`{2}/gy,tmpl:"<eq>$1</eq>",tag:"``"},{name:"math_inline",rex:/\$((?:\S?)|(?:\S.*?\S))\$/gy,tmpl:"<eq>$1</eq>",tag:"$",spaceEnclosed:!1,pre:texmath.$_pre,post:texmath.$_post}],block:[{name:"math_block_eqno",rex:/`{3}math\s+?([^`]+?)\s+?`{3}\s*?\(([^)$\r\n]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"```math"},{name:"math_block",rex:/`{3}math\s+?([^`]+?)\s+?`{3}/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"```math"}]},kramdown:{inline:[{name:"math_inline",rex:/\${2}(.+?)\${2}/gy,tmpl:"<eq>$1</eq>",tag:"$$"}],block:[{name:"math_block_eqno",rex:/\${2}([^$]+?)\${2}\s*?\(([^)\s]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"$$"},{name:"math_block",rex:/\${2}([^$]+?)\${2}/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"$$"}]},beg_end:{inline:[],block:[{name:"math_block",rex:/(\\(?:begin)\{([a-z]+)\}[\s\S]+?\\(?:end)\{\2\})/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"\\"}]},dollars:{inline:[{name:"math_inline_double",rex:/\${2}([^$]*?[^\\])\${2}/gy,tmpl:"<section><eqn>$1</eqn></section>",tag:"$$",displayMode:!0,pre:texmath.$_pre,post:texmath.$_post},{name:"math_inline",rex:/\$((?:[^\s\\])|(?:\S.*?[^\s\\]))\$/gy,tmpl:"<eq>$1</eq>",tag:"$",outerSpace:!1,pre:texmath.$_pre,post:texmath.$_post}],block:[{name:"math_block_eqno",rex:/\${2}([^$]*?[^\\])\${2}\s*?\(([^)\s]+?)\)/gmy,tmpl:'<section class="eqno"><eqn>$1</eqn><span>($2)</span></section>',tag:"$$"},{name:"math_block",rex:/\${2}([^$]*?[^\\])\${2}/gmy,tmpl:"<section><eqn>$1</eqn></section>",tag:"$$"}]}};
        // from morphdom@2.7.2
        {let e,t=11,l=(e,l)=>{let n,i,r,d,o,a=l.attributes;if(l.nodeType===t||e.nodeType===t)return;for(let t=a.length-1;t>=0;t--)n=a[t],i=n.name,r=n.namespaceURI,d=n.value,r?(i=n.localName||i,o=e.getAttributeNS(r,i),o!==d&&("xmlns"===n.prefix&&(i=n.name),e.setAttributeNS(r,i,d))):(o=e.getAttribute(i),o!==d&&e.setAttribute(i,d));let u=e.attributes;for(let t=u.length-1;t>=0;t--)n=u[t],i=n.name,r=n.namespaceURI,r?(i=n.localName||i,l.hasAttributeNS(r,i)||e.removeAttributeNS(r,i)):l.hasAttribute(i)||e.removeAttribute(i)},n="http://www.w3.org/1999/xhtml",i="undefined"==typeof document?void 0:document,r=!!i&&"content"in i.createElement("template"),d=!!i&&i.createRange&&"createContextualFragment"in i.createRange(),o=e=>{let t=i.createElement("template");return t.innerHTML=e,t.content.childNodes[0]},a=t=>(e||(e=i.createRange(),e.selectNode(i.body)),e.createContextualFragment(t).childNodes[0]),u=e=>{let t=i.createElement("body");return t.innerHTML=e,t.childNodes[0]},s=e=>(e=e.trim(),r?o(e):d?a(e):u(e)),f=(e,t)=>{let l,n,i=e.nodeName,r=t.nodeName;return i===r||(l=i.charCodeAt(0),n=r.charCodeAt(0),l<=90&&n>=97?i===r.toUpperCase():n<=90&&l>=97&&r===i.toUpperCase())},c=(e,t)=>t&&t!==n?i.createElementNS(t,e):i.createElement(e),m=(e,t)=>{let l=e.firstChild;for(;l;){let e=l.nextSibling;t.appendChild(l),l=e}return t},p=(e,t,l)=>{e[l]!==t[l]&&(e[l]=t[l],e[l]?e.setAttribute(l,""):e.removeAttribute(l))},h={OPTION:(e,t)=>{let l=e.parentNode;if(l){let n=l.nodeName.toUpperCase();"OPTGROUP"===n&&(l=l.parentNode,n=l&&l.nodeName.toUpperCase()),"SELECT"!==n||l.hasAttribute("multiple")||(e.hasAttribute("selected")&&!t.selected&&(e.setAttribute("selected","selected"),e.removeAttribute("selected")),l.selectedIndex=-1)}p(e,t,"selected")},INPUT:(e,t)=>{p(e,t,"checked"),p(e,t,"disabled"),e.value!==t.value&&(e.value=t.value),t.hasAttribute("value")||e.removeAttribute("value")},TEXTAREA:(e,t)=>{let l=t.value;e.value!==l&&(e.value=l);let n=e.firstChild;if(n){let t=n.nodeValue;if(t==l||!l&&t==e.placeholder)return;n.nodeValue=l}},SELECT:(e,t)=>{if(!t.hasAttribute("multiple")){let t,l,n=-1,i=0,r=e.firstChild;for(;r;)if(l=r.nodeName&&r.nodeName.toUpperCase(),"OPTGROUP"===l)t=r,r=t.firstChild;else{if("OPTION"===l){if(r.hasAttribute("selected")){n=i;break}i++}r=r.nextSibling,!r&&t&&(r=t.nextSibling,t=null)}e.selectedIndex=n}}},N=1,b=11,A=3,C=8,T=()=>{},g=e=>{if(e)return e.getAttribute&&e.getAttribute("id")||e.id},E=e=>function(t,l,n){if(n||(n={}),"string"==typeof l)if("#document"===t.nodeName||"HTML"===t.nodeName||"BODY"===t.nodeName){let e=l;(l=i.createElement("html")).innerHTML=e}else l=s(l);else l.nodeType===b&&(l=l.firstElementChild);let r=n.getNodeKey||g,d=n.onBeforeNodeAdded||T,o=n.onNodeAdded||T,a=n.onBeforeElUpdated||T,u=n.onElUpdated||T,p=n.onBeforeNodeDiscarded||T,E=n.onNodeDiscarded||T,v=n.onBeforeElChildrenUpdated||T,x=n.skipFromChildren||T,y=n.addChild||function(e,t){return e.appendChild(t)},S=!0===n.childrenOnly,U=Object.create(null),O=[],R=e=>{O.push(e)},V=(e,t)=>{if(e.nodeType===N){let l=e.firstChild;for(;l;){let e;t&&(e=r(l))?R(e):(E(l),l.firstChild&&V(l,t)),l=l.nextSibling}}},w=(e,t,l)=>{!1!==p(e)&&(t&&t.removeChild(e),E(e),V(e,l))},I=e=>{if(e.nodeType===N||e.nodeType===b){let t=e.firstChild;for(;t;){let e=r(t);e&&(U[e]=t),I(t),t=t.nextSibling}}};I(t);let P=e=>{o(e);let t=e.firstChild;for(;t;){let e=t.nextSibling,l=r(t);if(l){let e=U[l];e&&f(t,e)?(t.parentNode.replaceChild(e,t),B(e,t)):P(t)}else P(t);t=e}},B=(t,l,n)=>{let i=r(l);if(i&&delete U[i],!n){if(!1===a(t,l))return;if(e(t,l),u(t),!1===v(t,l))return}"TEXTAREA"!==t.nodeName?L(t,l):h.TEXTAREA(t,l)},L=(e,t)=>{let l,n,o,a,u,s=x(e,t),c=t.firstChild,m=e.firstChild;e:for(;c;){for(a=c.nextSibling,l=r(c);!s&&m;){if(o=m.nextSibling,c.isEqualNode&&c.isEqualNode(m)){c=a,m=o;continue e}n=r(m);let t,i=m.nodeType;if(i===c.nodeType&&(i===N?(l?l!==n&&((u=U[l])?o===u?t=!1:(e.insertBefore(u,m),n?R(n):w(m,e,!0),m=u,n=r(m)):t=!1):n&&(t=!1),t=!1!==t&&f(m,c),t&&B(m,c)):i!==A&&i!=C||(t=!0,m.nodeValue!==c.nodeValue&&(m.nodeValue=c.nodeValue))),t){c=a,m=o;continue e}n?R(n):w(m,e,!0),m=o}if(l&&(u=U[l])&&f(u,c))s||y(e,u),B(u,c);else{let t=d(c);!1!==t&&(t&&(c=t),c.actualize&&(c=c.actualize(e.ownerDocument||i)),y(e,c),P(c))}c=a,m=o}((e,t,l)=>{for(;t;){let n=t.nextSibling;(l=r(t))?R(l):w(t,e,!0),t=n}})(e,m,n);let p=h[e.nodeName];p&&p(e,t)},D=t,z=D.nodeType,H=l.nodeType;if(!S)if(z===N)H===N?f(t,l)||(E(t),D=m(t,c(l.nodeName,l.namespaceURI))):D=l;else if(z===A||z===C){if(H===z)return D.nodeValue!==l.nodeValue&&(D.nodeValue=l.nodeValue),D;D=l}if(B(D,l,S),O)for(let e=0,t=O.length;e<t;e++){let t=U[O[e]];t&&w(t,t.parentNode,!1)}return!S&&D!==t&&t.parentNode&&(D.actualize&&(D=D.actualize(t.ownerDocument||i)),t.parentNode.replaceChild(D,t)),D};window.morphdom=E(l)};   
        // from sha1-uint8array@0.10.7
        {const K=[1518500249,1859775393,-1894007588,-899497514];window.createSHA1=()=>new Hash;class Hash{constructor(){this.A=1732584193,this.B=-271733879,this.C=-1732584194,this.D=271733878,this.E=-1009589776,this._size=0,this._sp=0,(!sharedBuffer||sharedOffset>=8e3)&&(sharedBuffer=new ArrayBuffer(8e3),sharedOffset=0),this._byte=new Uint8Array(sharedBuffer,sharedOffset,80),this._word=new Int32Array(sharedBuffer,sharedOffset,20),sharedOffset+=80};update(t){if("string"==typeof t)return this._utf8(t);if(null==t)throw new TypeError("Invalid type: "+typeof t);const s=t.byteOffset,e=t.byteLength;let r=e/64|0,i=0;if(r&&!(3&s)&&!(this._size%64)){const e=new Int32Array(t.buffer,s,16*r);for(;r--;)this._int32(e,i>>2),i+=64;this._size+=i}if(1!==t.BYTES_PER_ELEMENT&&t.buffer){const r=new Uint8Array(t.buffer,s+i,e-i);return this._uint8(r)}return i===e?this:this._uint8(t,i)};_uint8(t,s){const{_byte:e,_word:r}=this,i=t.length;for(s|=0;s<i;){const h=this._size%64;let n=h;for(;s<i&&n<64;)e[n++]=t[s++];n>=64&&this._int32(r),this._size+=n-h}return this};_utf8(t){const{_byte:s,_word:e}=this,r=t.length;let i=this._sp;for(let h=0;h<r;){const n=this._size%64;let f=n;for(;h<r&&f<64;){let e=0|t.charCodeAt(h++);e<128?s[f++]=e:e<2048?(s[f++]=192|e>>>6,s[f++]=128|63&e):e<55296||e>57343?(s[f++]=224|e>>>12,s[f++]=128|e>>>6&63,s[f++]=128|63&e):i?(e=((1023&i)<<10)+(1023&e)+65536,s[f++]=240|e>>>18,s[f++]=128|e>>>12&63,s[f++]=128|e>>>6&63,s[f++]=128|63&e,i=0):i=e}f>=64&&(this._int32(e),e[0]=e[16]),this._size+=f-n}return this._sp=i,this};_int32(t,s){let{A:e,B:r,C:i,D:h,E:n}=this,f=0;for(s|=0;f<16;)W[f++]=swap32(t[s++]);for(f=16;f<80;f++)W[f]=rotate1(W[f-3]^W[f-8]^W[f-14]^W[f-16]);for(f=0;f<80;f++){const t=f/20|0,s=rotate5(e)+ft(t,r,i,h)+n+W[f]+K[t]|0;n=h,h=i,i=rotate30(r),r=e,e=s}this.A=e+this.A|0,this.B=r+this.B|0,this.C=i+this.C|0,this.D=h+this.D|0,this.E=n+this.E|0};digest(t){const{_byte:s,_word:e}=this;let r=this._size%64|0;for(s[r++]=128;3&r;)s[r++]=0;if(r>>=2,r>14){for(;r<16;)e[r++]=0;r=0,this._int32(e)}for(;r<16;)e[r++]=0;const i=8*this._size,h=(4294967295&i)>>>0,n=(i-h)/4294967296;return n&&(e[14]=swap32(n)),h&&(e[15]=swap32(h)),this._int32(e),"hex"===t?this._hex():this._bin()};_hex(){const{A:t,B:s,C:e,D:r,E:i}=this;return hex32(t)+hex32(s)+hex32(e)+hex32(r)+hex32(i)};_bin(){const{A:t,B:s,C:e,D:r,E:i,_byte:h,_word:n}=this;return n[0]=swap32(t),n[1]=swap32(s),n[2]=swap32(e),n[3]=swap32(r),n[4]=swap32(i),h.slice(0,20)}};const W=new Int32Array(80);let sharedBuffer,sharedOffset=0;const hex32=t=>(t+4294967296).toString(16).substr(-8),swapLE=t=>t<<24&4278190080|t<<8&16711680|t>>8&65280|t>>24&255,swapBE=t=>t,swap32=isBE()?swapBE:swapLE,rotate1=t=>t<<1|t>>>31,rotate5=t=>t<<5|t>>>27,rotate30=t=>t<<30|t>>>2;function ft(t,s,e,r){return 0===t?s&e|~s&r:2===t?s&e|s&r|e&r:s^e^r}function isBE(){return 254===new Uint8Array(new Uint16Array([65279]).buffer)[0]}};
        // from sha256-uint8array@0.10.7
        {const K=[1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998],algorithms={sha256:1};window.createSHA256=()=>new Hash;class Hash{constructor(){this.A=1779033703,this.B=-1150833019,this.C=1013904242,this.D=-1521486534,this.E=1359893119,this.F=-1694144372,this.G=528734635,this.H=1541459225,this._size=0,this._sp=0,(!sharedBuffer||sharedOffset>=8e3)&&(sharedBuffer=new ArrayBuffer(8e3),sharedOffset=0),this._byte=new Uint8Array(sharedBuffer,sharedOffset,80),this._word=new Int32Array(sharedBuffer,sharedOffset,20),sharedOffset+=80};update(t){if("string"==typeof t)return this._utf8(t);if(null==t)throw new TypeError("Invalid type: "+typeof t);const s=t.byteOffset,e=t.byteLength;let i=e/64|0,h=0;if(i&&!(3&s)&&!(this._size%64)){const e=new Int32Array(t.buffer,s,16*i);for(;i--;)this._int32(e,h>>2),h+=64;this._size+=h}if(1!==t.BYTES_PER_ELEMENT&&t.buffer){const i=new Uint8Array(t.buffer,s+h,e-h);return this._uint8(i)}return h===e?this:this._uint8(t,h)};_uint8(t,s){const{_byte:e,_word:i}=this,h=t.length;for(s|=0;s<h;){const r=this._size%64;let n=r;for(;s<h&&n<64;)e[n++]=t[s++];n>=64&&this._int32(i),this._size+=n-r}return this};_utf8(t){const{_byte:s,_word:e}=this,i=t.length;let h=this._sp;for(let r=0;r<i;){const n=this._size%64;let a=n;for(;r<i&&a<64;){let e=0|t.charCodeAt(r++);e<128?s[a++]=e:e<2048?(s[a++]=192|e>>>6,s[a++]=128|63&e):e<55296||e>57343?(s[a++]=224|e>>>12,s[a++]=128|e>>>6&63,s[a++]=128|63&e):h?(e=((1023&h)<<10)+(1023&e)+65536,s[a++]=240|e>>>18,s[a++]=128|e>>>12&63,s[a++]=128|e>>>6&63,s[a++]=128|63&e,h=0):h=e}a>=64&&(this._int32(e),e[0]=e[16]),this._size+=a-n}return this._sp=h,this};_int32(t,s){let{A:e,B:i,C:h,D:r,E:n,F:a,G:f,H:o}=this,_=0;for(s|=0;_<16;)W[_++]=swap32(t[s++]);for(_=16;_<64;_++)W[_]=gamma1(W[_-2])+W[_-7]+gamma0(W[_-15])+W[_-16]|0;for(_=0;_<64;_++){const t=o+sigma1(n)+ch(n,a,f)+K[_]+W[_]|0,s=sigma0(e)+maj(e,i,h)|0;o=f,f=a,a=n,n=r+t|0,r=h,h=i,i=e,e=t+s|0}this.A=e+this.A|0,this.B=i+this.B|0,this.C=h+this.C|0,this.D=r+this.D|0,this.E=n+this.E|0,this.F=a+this.F|0,this.G=f+this.G|0,this.H=o+this.H|0};digest(t){const{_byte:s,_word:e}=this;let i=this._size%64|0;for(s[i++]=128;3&i;)s[i++]=0;if(i>>=2,i>14){for(;i<16;)e[i++]=0;i=0,this._int32(e)}for(;i<16;)e[i++]=0;const h=8*this._size,r=(4294967295&h)>>>0,n=(h-r)/4294967296;return n&&(e[14]=swap32(n)),r&&(e[15]=swap32(r)),this._int32(e),"hex"===t?this._hex():this._bin()};_hex(){const{A:t,B:s,C:e,D:i,E:h,F:r,G:n,H:a}=this;return hex32(t)+hex32(s)+hex32(e)+hex32(i)+hex32(h)+hex32(r)+hex32(n)+hex32(a)};_bin(){const{A:t,B:s,C:e,D:i,E:h,F:r,G:n,H:a,_byte:f,_word:o}=this;return o[0]=swap32(t),o[1]=swap32(s),o[2]=swap32(e),o[3]=swap32(i),o[4]=swap32(h),o[5]=swap32(r),o[6]=swap32(n),o[7]=swap32(a),f.slice(0,32)}}const W=new Int32Array(64);let sharedBuffer,sharedOffset=0;const hex32=t=>(t+4294967296).toString(16).substr(-8),swapLE=t=>t<<24&4278190080|t<<8&16711680|t>>8&65280|t>>24&255,swapBE=t=>t,swap32=isBE()?swapBE:swapLE,ch=(t,s,e)=>e^t&(s^e),maj=(t,s,e)=>t&s|e&(t|s),sigma0=t=>(t>>>2|t<<30)^(t>>>13|t<<19)^(t>>>22|t<<10),sigma1=t=>(t>>>6|t<<26)^(t>>>11|t<<21)^(t>>>25|t<<7),gamma0=t=>(t>>>7|t<<25)^(t>>>18|t<<14)^t>>>3,gamma1=t=>(t>>>17|t<<15)^(t>>>19|t<<13)^t>>>10;function isBE(){return 254===new Uint8Array(new Uint16Array([65279]).buffer)[0]}}
    </script>
    <script>
        const stringToArray = string => {
            let arr = []
            for (let i = 0; i < string.length; i++) {
                arr.push(string.charCodeAt(i));
            }
            return arr
        }
        const stringToUint = string => {
            return new Uint8Array(stringToArray(string));
        }
        const uintToString = uintArray => {
            let str = "";
            let len = Math.ceil(uintArray.byteLength / 32767);
            for (let i = 0; i < len; i++) {
                str += String.fromCharCode.apply(null, uintArray.subarray(i * 32767, Math.min((i + 1) * 32767, uintArray.byteLength)));
            }
            return str;
        }
        let isCompressedChats = localStorage.getItem("compressedChats") === "true";
        const originSetItem = localStorage.setItem;
        localStorage.setItem = (key, value) => {
            try {
                if (isCompressedChats && key === "chats") value = uintToString(deflateSync(new TextEncoder().encode(value), { level: 1 }));
                originSetItem.call(localStorage, key, value)
            } catch (e) {
                if (isCompressedChats) {
                    notyf.error(translations[locale]["localQuotaExceedTip"])
                    return;
                }
                let isKeyChats = key === "chats";
                let compressed = uintToString(deflateSync(new TextEncoder().encode(isKeyChats ? value : localStorage.getItem("chats")), { level: 1 }));
                originSetItem.call(localStorage, "chats", compressed);
                originSetItem.call(localStorage, "compressedChats", true);
                isCompressedChats = true;
                if (!isKeyChats) originSetItem.call(localStorage, key, value);
            }
        }
    </script>
    <script>
        const localeList = ["en", "zh"];
        let locale; // UI语言
        const setLangEle = document.getElementById("setLang");
        const setLang = () => {
            let langClass = locale + "Lang";
            localStorage.setItem("UILang", locale)
            document.documentElement.lang = locale === "zh" ? "zh-CN" : "en";
            setLangEle.classList = "setDetail themeDetail langDetail " + langClass;
        }
        setLangEle.onclick = (ev) => {
            let idx = Array.prototype.indexOf.call(setLangEle.children, ev.target);
            if (locale !== localeList[idx]) {
                locale = localeList[idx];
                setLang();
                changeLocale();
            }
        }
        const initLang = () => {
            let localLang = localStorage.getItem("UILang") || (navigator.language.startsWith("zh-") ? "zh" : "en");
            let isInit = locale === void 0;
            if (locale !== localLang) {
                locale = localLang;
                if (!isInit) changeLocale();
            };
            setLang();
        }
        initLang();
        const translations = {
            "en": {
                "description": "Simple and powerful ChatGPT app",
                "newChat": "New chat",
                "newChatName": "New chat",
                "newFolder": "New folder",
                "newFolderName": "New folder",
                "search": "Search",
                "matchCaseTip": "Match case",
                "forceRe": "Force refresh",
                "clearAll": "Clear all chats",
                "setting": "Setting",
                "nav": "Navigate",
                "winedWin": "Window",
                "fullWin": "Full screen",
                "quickSet": "Quick setting",
                "chat": "Chat",
                "tts": "TTS",
                "stt": "STT",
                "avatar": "Avatar",
                "systemRole": "System role",
                "presetRole": "Preset",
                "default": "Default",
                "assistant": "Assistant",
                "cat": "Cat girl",
                "emoji": "Emoji",
                "withImg": "Image",
                "defaultText": "",
                "assistantText": "You are a helpful assistant, answer as concisely as possible.",
                "catText": "You are a cute cat girl, you must end every sentence with 'meow'",
                "emojiText": "Your personality is very lively, there must be at least one emoji icon in every sentence",
                "imageText": "When you need to send pictures, please generate them in markdown language, without backslashes or code boxes. When you need to use the unsplash API, follow the format, https://source.unsplash.com/960x640/?<English keywords>",
                "nature": "Nature",
                "natureNeg": "Accurate",
                "naturePos": "Creativity",
                "quality": "Quality",
                "qualityNeg": "Repetitive",
                "qualityPos": "Nonsense",
                "chatsWidth": "Chats width",
                "typeSpeed": "Typing speed",
                "continuousLen": "Context messages",
                "msgAbbr": " msgs.",
                "slow": "Slow",
                "fast": "Fast",
                "COT": "Chain of Thought",
                "thinking": "Thinking...",
                "thinked": "Deeply thought",
                "longReply": "Long reply",
                "ttsService": "TTS API",
                "sttService": "STT API",
                "openaiTTS": "OpenAI",
                "azureTTS": "Azure",
                "edgeTTS": "Edge",
                "systemTTS": "System",
                "azureRegion": "Azure region",
                "azureKey": "Azure key",
                "loadVoice": "Load voice",
                "voiceName": "Switch",
                "userVoice": "User voice",
                "replyVoice": "Reply voice",
                "TTSTest": "Hello, nice to meet you.",
                "play": "Play",
                "pause": "Pause",
                "resume": "Resume",
                "stop": "Stop",
                "style": "Style",
                "role": "Role",
                "volume": "Volume",
                "low": "Low",
                "high": "High",
                "rate": "Rate",
                "slow": "Slow",
                "fast": "Fast",
                "pitch": "Pitch",
                "neutral": "Neutral",
                "intense": "Intense",
                "contSpeech": "Continuous speech",
                "autoSpeech": "Auto speech",
                "unsupportRecTip": "Voice recognition is not supported in the current environment. Please refer to the documentation.",
                "loadRecVoice": "Load language",
                "lang": "Language",
                "dialect": "Dialect",
                "autoSendKey": "Auto send keyword",
                "autoStopKey": "Auto stop keyword",
                "autoSendDelay": "Auto send delay time",
                "second": "s",
                "keepListenMic": "Keep listen",
                "send": "Send",
                "askTip": "Type message here",
                "clearChat": "Clear chat",
                "models": "Models",
                "ui": "UI",
                "hotkey": "Hotkey",
                "data": "Data",
                "theme": "Theme",
                "darkTheme": "Dark",
                "lightTheme": "Light",
                "autoWord": "Auto",
                "systemTheme": "System",
                "customDarkTheme": "Custom dark theme",
                "startDark": "Start",
                "endDark": "End",
                "aiEndpoint": " endpoint",
                "aiKey": " API key",
                "aiModel": "Custom model name",
                "used": "Used ",
                "available": "Avail ",
                "navKey": "Toggle nav",
                "fullKey": "Window size",
                "themeKey": "Toggle theme",
                "langKey": "Toggle lang",
                "inputKey": "Message",
                "voiceKey": "Voice",
                "resetTip": "Restore default",
                "recKey": "Recognition",
                "speechKey": "Start speech",
                "export": "Export",
                "import": "Import",
                "clear": "Clear",
                "reset": "Reset",
                "localStore": "Local storage",
                "forceReTip": "Force refresh page?",
                "noSpeechTip": "No speech was detected. You may need to adjust your microphone settings.",
                "noMicTip": "No microphone was found. Ensure that a microphone is installed and microphone settings are configured correctly.",
                "noMicPerTip": "Permission to use microphone is blocked.",
                "azureInvalidTip": "Invalid access key or wrong Azure region endpoint, please check!",
                "errorAiKeyTip": "Invalid or incorrect API key, please check API key!",
                "copyCode": "Copy code",
                "copySuccess": "Success",
                "update": "Update",
                "cancel": "Cancel",
                "delMsgTip": "Delete this message?",
                "edit": "Edit",
                "refresh": "Refresh",
                "continue": "Continue",
                "copy": "Copy",
                "del": "Delete",
                "downAudio": "Download audio",
                "speech": "Speech",
                "chats": " chats",
                "delFolderTip": "Delete this folder?",
                "delChatTip": "Delete this chat?",
                "exportSuccTip": "Export successful!",
                "importSuccTip": "Import successful!",
                "importFailTip": "Import failed, please check the file format!",
                "clearChatSuccTip": "Clear chats data successful!",
                "resetSetSuccTip": "Reset settings successful!",
                "clearAllTip": "Delete all chats and folders?",
                "resetSetTip": "Restore all settings to default?",
                "hotkeyConflict": "Hotkey conflict, please choose another key!",
                "customDarkTip": "Start time and end time cannot be the same!",
                "timeoutTip": "Request timeout, please try again later!",
                "largeReqTip": "Request is too large, please delete part of the chat or cancel continuous chat!",
                "noModelPerTip": "Not permission to use this model, please choose another GPT model!",
                "apiRateTip": "Trigger API call rate limit, please try again later!",
                "exceedLimitTip": "API usage exceeded limit, please check your bill!",
                "badGateTip": "Gateway error or timeout, please try again later!",
                "badEndpointTip": "Failed to access the endpoint, please check the endpoint!",
                "clearChatTip": "Clear this chat?",
                "cantSpeechTip": "Current voice cannot synthesize this message, please choose another voice or message!",
                "cantTranscribeTip": "Voice recognition failed, please try again!",
                "noStreamTip": "Automatic features aren't available for non-realtime speech recognition service!",
                "localQuotaExceedTip": "Local storage exceeded limit, please export chats data and clear or delete some chats!",
            },
            "zh": {
                "description": "简洁而强大的ChatGPT应用",
                "newChat": "新建会话",
                "newChatName": "新的会话",
                "newFolder": "新建文件夹",
                "newFolderName": "新文件夹",
                "search": "搜索",
                "matchCaseTip": "区分大小写",
                "forceRe": "强制刷新",
                "clearAll": "清空全部",
                "setting": "设置",
                "nav": "导航",
                "winedWin": "窗口",
                "fullWin": "全屏",
                "quickSet": "快速设置",
                "chat": "会话",
                "tts": "语音合成",
                "stt": "语音识别",
                "avatar": "用户头像",
                "systemRole": "系统角色",
                "presetRole": "预设角色",
                "default": "默认",
                "assistant": "助手",
                "cat": "猫娘",
                "emoji": "表情",
                "withImg": "有图",
                "defaultText": "",
                "assistantText": "你是一个乐于助人的助手，尽量简明扼要地回答",
                "catText": "你是一个可爱的猫娘，每句话结尾都要带个'喵'",
                "emojiText": "你的性格很活泼，每句话中都要有至少一个emoji图标",
                "imageText": "当你需要发送图片的时候，请用 markdown 语言生成，不要反斜线，不要代码框，需要使用 unsplash API时，遵循一下格式， https://source.unsplash.com/960x640/? ＜英文关键词＞",
                "nature": "角色性格",
                "natureNeg": "准确严谨",
                "naturePos": "灵活创新",
                "quality": "回答质量",
                "qualityNeg": "重复保守",
                "qualityPos": "胡言乱语",
                "chatsWidth": "会话宽度",
                "typeSpeed": "打字机速度",
                "continuousLen": "上下文消息数",
                "msgAbbr": "条",
                "slow": "慢",
                "fast": "快",
                "COT": "思维链",
                "thinking": "思考中...",
                "thinked": "已深度思考",
                "longReply": "长回复",
                "ttsService": "语音合成服务",
                "sttService": "语音识别服务",
                "openaiTTS": "OpenAI语音",
                "azureTTS": "Azure语音",
                "edgeTTS": "Edge语音",
                "systemTTS": "系统语音",
                "azureRegion": "Azure区域",
                "azureKey": "Azure密钥",
                "loadVoice": "加载语音",
                "voiceName": "切换",
                "userVoice": "用户语音",
                "replyVoice": "回答语音",
                "TTSTest": "你好，很高兴认识你。",
                "play": "播放",
                "pause": "暂停",
                "resume": "恢复",
                "stop": "停止",
                "style": "风格",
                "role": "角色",
                "volume": "音量",
                "low": "低",
                "high": "高",
                "rate": "语速",
                "slow": "慢",
                "fast": "快",
                "pitch": "音调",
                "neutral": "平淡",
                "intense": "起伏",
                "contSpeech": "连续朗读",
                "autoSpeech": "自动朗读",
                "unsupportRecTip": "当前环境不支持语音识别，请查阅文档。",
                "loadRecVoice": "加载语言",
                "lang": "语言",
                "dialect": "方言",
                "autoSendKey": "自动发送关键词",
                "autoStopKey": "自动停止关键词",
                "autoSendDelay": "自动发送延迟时间",
                "second": "秒",
                "keepListenMic": "保持监听",
                "send": "发送",
                "askTip": "来问点什么吧",
                "clearChat": "清空会话",
                "models": "模型",
                "ui": "界面",
                "hotkey": "快捷键",
                "data": "数据",
                "theme": "主题",
                "darkTheme": "深色",
                "lightTheme": "浅色",
                "autoWord": "自动",
                "systemTheme": "跟随系统",
                "customDarkTheme": "自定义深色主题时间",
                "startDark": "开始时间",
                "endDark": "结束时间",
                "aiEndpoint": "接口",
                "aiKey": "API密钥",
                "aiModel": "自定义模型",
                "used": "已用 ",
                "available": "可用 ",
                "navKey": "切换导航",
                "fullKey": "全屏/窗口",
                "themeKey": "切换主题",
                "langKey": "切换语言",
                "inputKey": "输入框",
                "voiceKey": "语音",
                "resetTip": "重置设置",
                "recKey": "语音输入",
                "speechKey": "朗读会话",
                "export": "导出",
                "import": "导入",
                "clear": "清空",
                "reset": "重置",
                "localStore": "本地存储",
                "forceReTip": "是否强制刷新页面？",
                "noSpeechTip": "未识别到语音，请调整麦克风后重试！",
                "noMicTip": "未识别到麦克风，请确保已安装麦克风！",
                "noMicPerTip": "未允许麦克风权限！",
                "azureInvalidTip": "Azure区域错误或密钥无效，请检查！",
                "errorAiKeyTip": "API密钥错误或失效，请检查API密钥！",
                "copyCode": "复制代码",
                "copySuccess": "复制成功",
                "update": "更新",
                "cancel": "取消",
                "delMsgTip": "是否删除此消息？",
                "edit": "编辑",
                "refresh": "刷新",
                "continue": "继续",
                "copy": "复制",
                "del": "删除",
                "downAudio": "下载语音",
                "speech": "朗读",
                "chats": "个会话",
                "delFolderTip": "是否删除此文件夹？",
                "delChatTip": "是否删除此会话？",
                "exportSuccTip": "导出成功！",
                "importSuccTip": "导入成功！",
                "importFailTip": "导入失败，请检查文件格式！",
                "clearChatSuccTip": "清空会话成功！",
                "resetSetSuccTip": "重置设置成功！",
                "clearAllTip": "是否删除所有会话和文件夹？",
                "resetSetTip": "是否还原所有设置为默认值？",
                "hotkeyConflict": "快捷键冲突，请选择其他键位！",
                "customDarkTip": "开始时间和结束时间不能相同！",
                "timeoutTip": "请求超时，请稍后重试！",
                "largeReqTip": "请求内容过大，请删除部分对话或关闭连续对话！",
                "noModelPerTip": "无权使用此模型，请选择其他GPT模型！",
                "apiRateTip": "触发API调用频率限制，请稍后重试！",
                "exceedLimitTip": "API使用超出限额，请检查您的账单！",
                "badGateTip": "网关错误或超时，请稍后重试！",
                "badEndpointTip": "访问接口失败，请检查接口！",
                "clearChatTip": "是否清空此会话？",
                "cantSpeechTip": "当前语音无法合成此消息，请选择其他语音或消息！",
                "cantTranscribeTip": "语音识别失败，请重试！",
                "noStreamTip": "非实时语音识别服务无法使用自动功能！",
                "localQuotaExceedTip": "本地存储超出限额，请导出会话并清空或删除部分会话！",
            },
        };
        const translateElement = (ele, type) => {
            const key = ele.getAttribute("data-i18n-" + type);
            const translation = translations[locale][key];
            if (type === "title") {
                ele.setAttribute("title", translation);
            } else if (type === "place") {
                ele.setAttribute("placeholder", translation);
            } else if (type === "value") {
                ele.setAttribute("value", translation);
            } else {
                ele.textContent = translation;
            }
        }
        const initLocale = () => {
            document.querySelectorAll("[data-i18n-title]").forEach(ele => { translateElement(ele, "title") });
            document.querySelectorAll("[data-i18n-place]").forEach(ele => { translateElement(ele, "place") });
            document.querySelectorAll("[data-i18n-value]").forEach(ele => { translateElement(ele, "value") });
            document.querySelectorAll("[data-i18n-key]").forEach(ele => { translateElement(ele, "key") });
            document.querySelectorAll("[data-i18n-theme]").forEach(ele => {
                let key = themeMode === 2 ? "autoWord" : themeMode === 1 ? "lightTheme" : "darkTheme";
                ele.setAttribute("title", translations[locale][key])
            })
            document.querySelectorAll("[data-i18n-window]").forEach(ele => {
                let key = isFull ? "winedWin" : "fullWin";
                ele.setAttribute("title", translations[locale][key])
            })
            document.head.children[3].setAttribute("content", translations[locale]["description"])
        };
        initLocale();
        const changeLocale = () => {
            initLocale();
            document.querySelectorAll("[data-type='chatEdit'],[data-type='folderEdit']").forEach(ele => {
                ele.children[0].textContent = translations[locale]["edit"];
            });
            document.querySelectorAll("[data-type='chatDel'],[data-type='folderDel']").forEach(ele => {
                ele.children[0].textContent = translations[locale]["del"];
            });
            document.querySelectorAll("[data-type='folderAddChat']").forEach(ele => {
                ele.children[0].textContent = translations[locale]["newChat"];
            });
            document.querySelectorAll("[data-id]").forEach(ele => {
                let key = ele.getAttribute("data-id");
                if (key.endsWith("Md")) {
                    if (key === "speechMd" || key === "pauseMd" || key === "resumeMd") {
                        ele.children[0].textContent = translations[locale][key.slice(0, -2)];
                    } else if (key === "refreshMd") {
                        ele.setAttribute("title", translations[locale][ele.classList.contains("refreshReq") ? "refresh" : "continue"]);
                    } else {
                        ele.setAttribute("title", translations[locale][key.slice(0, -2)]);
                    }
                }
            });
            document.querySelectorAll(".folderNum").forEach(ele => {
                let num = ele.textContent.match(/\d+/)[0];
                ele.textContent = num + translations[locale]["chats"];
            });
            document.querySelectorAll(".u-mdic-copy-btn").forEach(ele => {
                ele.setAttribute("text", translations[locale]["copyCode"]);
            })
            document.querySelectorAll(".u-mdic-copy-notify").forEach(ele => {
                ele.setAttribute("text", translations[locale]["copySuccess"]);
            })
            if (editingIdx !== void 0) {
                document.querySelector("[data-i18n-key='send']").textContent = translations[locale]["update"];
                document.querySelector("[data-i18n-title='clearChat']").setAttribute("title", translations[locale]["cancel"]);
            }
            loadPrompt();
        }
    </script>
    <script>
        const windowEle = document.getElementsByClassName("chat_window")[0];
        const messagesEle = document.getElementsByClassName("messages")[0];
        const chatlog = document.getElementById("chatlog");
        const stopEle = document.getElementById("stopChat");
        const sendBtnEle = document.getElementById("sendbutton");
        const clearEle = document.getElementsByClassName("clearConv")[0];
        const inputAreaEle = document.getElementById("chatinput");
        const settingEle = document.getElementById("setting");
        const dialogEle = document.getElementById("setDialog");
        const selectorEle = document.getElementById("selector");
        const modelSetEle = document.getElementById("modelDialog");
        const lightEle = document.getElementById("toggleLight");
        const setLightEle = document.getElementById("setLight");
        const autoThemeEle = document.getElementById("autoDetail");
        const systemEle = document.getElementById("systemInput");
        const speechServiceEle = document.getElementById("preSetService");
        const recServiceEle = document.getElementById("preRecService");
        const newChatEle = document.getElementById("newChat");
        const folderListEle = document.getElementById("folderList");
        const chatListEle = document.getElementById("chatList");
        const searchChatEle = document.getElementById("searchChat");
        const voiceRecEle = document.getElementById("voiceRecIcon");
        const voiceRecSetEle = document.getElementById("voiceRecSetting");
        const preEle = document.getElementById("preSetSystem");
        let voicesData; // 语音数据
        let voiceType = 1; // 设置 0: 提问语音，1：回答语音
        let voiceRole = []; // 语音
        let voiceTestText; // 测试语音文本
        let voiceVolume = []; //音量
        let voiceRate = []; // 语速
        let voicePitch = []; // 音调
        let enableContVoice; // 连续朗读
        let enableAutoVoice; // 自动朗读
        let existVoice = 2; // 4:OpenAI语音 3:Azure语音 2:edge在线语音, 1:本地语音, 0:不支持语音
        const azureRegions = ['southafricanorth', 'eastasia', 'southeastasia', 'australiaeast', 'centralindia', 'japaneast', 'japanwest', 'koreacentral', 'canadacentral', 'northeurope', 'westeurope', 'francecentral', 'germanywestcentral', 'norwayeast', 'swedencentral', 'switzerlandnorth', 'switzerlandwest', 'uksouth', 'uaenorth', 'brazilsouth', 'qatarcentral', 'centralus', 'eastus', 'eastus2', 'northcentralus', 'southcentralus', 'westcentralus', 'westus', 'westus2', 'westus3'];
        let azureRegion;
        let azureKey;
        let azureRole = [];
        let azureStyle = [];
        const supportSpe = !!(window.speechSynthesis && window.SpeechSynthesisUtterance);
        const isSafeEnv = window.isSecureContext; // 安全上下文
        const supportLocalRec = isSafeEnv && !!window.webkitSpeechRecognition; // 是否支持本地语音识别输入
        const supportOnlineRec = isSafeEnv && navigator.mediaDevices && navigator.mediaDevices.getUserMedia && window.AudioContext && ("audioWorklet" in window.AudioContext.prototype || "createScriptProcessor" in window.AudioContext.prototype);
        const supportOnlineLegacyRec = isSafeEnv && navigator.mediaDevices && navigator.mediaDevices.getUserMedia && !!window.MediaRecorder && (MediaRecorder.isTypeSupported("audio/webm") || MediaRecorder.isTypeSupported("audio/mp4"));
        const supportRec = supportLocalRec || supportOnlineRec || supportOnlineLegacyRec;
        let existRec = 1; // 2:Azure语音，1:系统语音
        let azureRecRegion;
        let azureRecKey;
        let recing = false;
        let autoSendWord; // 自动发送关键词
        let autoStopWord; // 自动停止关键词
        let autoSendTime; // 自动发送延迟时间
        let keepListenMic; // 保持监听麦克风
        let autoSendTimer;
        let resetRecRes;
        let toggleRecEv;
        const isAndroid = /\bAndroid\b/i.test(navigator.userAgent);
        const isApple = /(Mac|iPhone|iPod|iPad)/i.test(navigator.userAgent);
        const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
        const isPWA = navigator.standalone || window.matchMedia("(display-mode: standalone)").matches;
        if (isPWA) {
            let bottomEle = document.querySelector(".bottom_wrapper");
            let footerEle = document.querySelector(".navFooter");
            footerEle.style.marginBottom = bottomEle.style.marginBottom = "8px";
        };
        const dayMs = 8.64e7;
        refreshPage.onclick = () => {
            if (confirmAction(translations[locale]["forceReTip"])) {
                location.href = location.origin + location.pathname + "?" + new Date().getTime()
            }
        };
        const noLoading = () => {
            return !loading && (!currentResEle || currentResEle.dataset.loading !== "true")
        };
        const uuidv4 = (upper) => {
            let uuid = ([1e7] + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
                (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
            );
            return upper ? uuid.toUpperCase() : uuid;
        };
        if (!isMobile) inputAreaEle.focus();
        const textInputEvent = () => {
            if (noLoading()) sendBtnEle.classList.toggle("activeSendBtn", inputAreaEle.value.trim().length);
            inputAreaEle.style.height = "47px";
            inputAreaEle.style.height = inputAreaEle.scrollHeight + "px";
        };
        inputAreaEle.oninput = textInputEvent;
        const toggleNavEv = () => {
            let isShowNav = document.body.classList.toggle("show-nav");
            if (window.innerWidth > 800) {
                localStorage.setItem("pinNav", isShowNav)
            }
        }
        document.body.addEventListener("mousedown", event => {
            if (event.target.className === "toggler") {
                toggleNavEv();
            } else if (event.target.className === "overlay") {
                document.body.classList.remove("show-nav");
            } else if (event.target === document.body) {
                if (window.innerWidth <= 800) {
                    document.body.classList.remove("show-nav");
                }
            }
        });
        const endSetEvent = (ev) => {
            if (!document.getElementById("sysDialog").contains(ev.target)) {
                ev.preventDefault();
                ev.stopPropagation();
                endSet();
            }
        }
        const endSet = () => {
            document.getElementById("sysMask").style.display = "none";
            document.body.removeEventListener("click", endSetEvent, true);
        }
        document.getElementById("closeSet").onclick = endSet;
        document.getElementById("sysSetting").onclick = () => {
            document.getElementById("sysMask").style.display = "flex";
            checkStorage();
            document.getElementById("sysMask").onmousedown = endSetEvent;
        };
        const setAutoTimer = () => {
            if (autoSendTime) {
                autoSendTimer = setTimeout(() => {
                    genFunc();
                    autoSendTimer = void 0;
                }, autoSendTime * 1000);
            }
        }
        const clearAutoSendTimer = () => {
            if (autoSendTimer !== void 0) {
                clearTimeout(autoSendTimer);
                autoSendTimer = void 0;
            }
        }
        if (!supportLocalRec) recServiceEle.remove(2);
        if (!supportOnlineRec) recServiceEle.remove(1);
        if (!supportOnlineLegacyRec) recServiceEle.remove(0);
        const initRecVal = () => {
            if (!supportRec) {
                noRecTip.style.display = "block"
                noRecTip.parentElement.firstElementChild.style.display = "none";
                noRecTip.parentElement.children[1].style.display = "none";
                return;
            }
            let localRecType = localStorage.getItem("existRec");
            recServiceEle.value = existRec = parseInt(localRecType || (supportLocalRec ? "1" : "2"));
        }
        initRecVal();
        const clearAzureRec = () => {
            azureRecKey = void 0;
            localStorage.removeItem(azureRecRegion + "RecData");
            azureRecData = void 0;
            azureRecRegion = void 0;
            azureRecKeyInput.parentElement.style.display = "none";
            preRecAzureRegion.parentElement.style.display = "none";
        }
        const featStreamRec = (hide) => {
            document.querySelectorAll('[data-feat="forStream"]').forEach(item => item.style.display = (hide ? "none" : "block"))
            document.querySelectorAll('[data-feat="forNoStream"]').forEach(item => item.style.display = (hide ? "block" : "none"))
        }
        let azureRecData, systemRecData, checkAzureRecAbort;
        const toggleRecCheck = (bool) => {
            checkRecLoad.style.display = bool ? "flex" : "none";
            recDetail.style.display = bool ? "none" : "block";
            hotKeyVoiceRec.parentElement.style.display = bool ? "none" : "block";
            document.getElementById("voiceRec").style.display = bool ? "none" : "block";
            if (bool) inputAreaEle.classList.remove("message_if_voice");
            else inputAreaEle.classList.add("message_if_voice");
        }
        recServiceEle.onchange = () => {
            if (!supportRec) return;
            existRec = parseInt(recServiceEle.value);
            localStorage.setItem("existRec", existRec);
            toggleRecCheck(true);
            if (checkAzureRecAbort && !checkAzureRecAbort.signal.aborted) {
                checkAzureRecAbort.abort();
                checkAzureRecAbort = void 0;
            }
            if (existRec === 3) {
                clearAzureRec();
                loadOpenAIRec();
                featStreamRec(true);
            } else if (existRec === 2) {
                azureRecKeyInput.parentElement.style.display = "block";
                preRecAzureRegion.parentElement.style.display = "block";
                loadAzureRec();
                featStreamRec();
            } else {
                clearAzureRec();
                loadLocalRec();
                featStreamRec();
            }
        }
        const loadLocalRec = () => { initRecSetting() };
        const loadOpenAIRec = () => { initRecSetting() };
        const loadAzureRec = () => {
            let checking = false;
            const checkAzureFunc = () => {
                if (checking) return;
                if (azureRecKey) {
                    checking = true;
                    checkRecLoad.classList.add("voiceChecking");
                    checkAzureRecAbort = new AbortController();
                    setTimeout(() => {
                        if (checkAzureRecAbort && !checkAzureRecAbort.signal.aborted) {
                            checkAzureRecAbort.abort();
                            checkAzureRecAbort = void 0;
                        }
                    }, 15000);
                    getAzureToken(checkAzureRecAbort.signal).then(() => {
                        getRecList(checkAzureRecAbort.signal).then(() => {
                            initRecSetting(azureRecData);
                        }).catch(e => {
                        }).finally(() => {
                            checkRecLoad.classList.remove("voiceChecking");
                            checking = false;
                        })
                    }).catch(e => {
                    }).finally(() => {
                        checkRecLoad.classList.remove("voiceChecking");
                        checking = false;
                    })
                }
            };
            checkRecLoad.onclick = checkAzureFunc;
            const getAzureToken = (signal) => {
                return new Promise((res, rej) => {
                    fetch("https://" + azureRecRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken", {
                        signal,
                        method: "POST",
                        headers: {
                            "Ocp-Apim-Subscription-Key": azureRecKey
                        }
                    }).then(response => {
                        response.text().then(text => {
                            try {
                                let json = JSON.parse(text);
                                notyf.error(translations[locale]["azureInvalidTip"]);
                                rej();
                            } catch (e) {
                                res();
                            }
                        });
                    }).catch(e => {
                        localStorage.removeItem(azureRecRegion + "RecData");
                        azureRecData = void 0;
                        rej();
                    })
                })
            };
            const getRecList = (signal) => {
                return new Promise((res, rej) => {
                    if (azureRecData) res();
                    else {
                        let localAzureRecData = localStorage.getItem(azureRecRegion + "RecData");
                        if (localAzureRecData) {
                            azureRecData = JSON.parse(localAzureRecData);
                            res();
                        } else {
                            fetch("https://" + azureRecRegion + ".stt.speech.microsoft.com/api/v1.0/languages/recognition", {
                                signal
                            }).then(response => {
                                response.json().then(json => {
                                    azureRecData = json;
                                    localStorage.setItem(azureRecRegion + "RecData", JSON.stringify(json));
                                    res();
                                }).catch(e => {
                                    notyf.error(translations[locale]["azureInvalidTip"]);
                                    rej();
                                })
                            }).catch(e => {
                                localStorage.removeItem(azureRecRegion + "RecData");
                                azureRecData = void 0;
                                rej();
                            })
                        }
                    }
                })
            };
            let azureRecRegionEle = document.getElementById("preRecAzureRegion");
            if (!azureRecRegionEle.options.length) {
                azureRegions.forEach((region, i) => {
                    let option = document.createElement("option");
                    option.value = region;
                    option.text = region;
                    azureRecRegionEle.options.add(option);
                });
            }
            let localAzureRecRegion = localStorage.getItem("azureRecRegion");
            if (localAzureRecRegion) {
                azureRecRegion = localAzureRecRegion;
                azureRecRegionEle.value = localAzureRecRegion;
            }
            azureRecRegionEle.onchange = () => {
                azureRecRegion = azureRecRegionEle.value;
                localStorage.setItem("azureRecRegion", azureRecRegion);
                toggleRecCheck(true);
            }
            azureRecRegionEle.dispatchEvent(new Event("change"));
            let azureRecKeyEle = document.getElementById("azureRecKeyInput");
            let localAzureRecKey = localStorage.getItem("azureRecKey");
            if (localAzureRecKey) {
                azureRecKey = localAzureRecKey;
                azureRecKeyEle.value = localAzureRecKey;
            }
            azureRecKeyEle.onchange = () => {
                azureRecKey = azureRecKeyEle.value;
                localStorage.setItem("azureRecKey", azureRecKey);
                toggleRecCheck(true);
            }
            azureRecKeyEle.dispatchEvent(new Event("change"));
            if (azureRecKey) checkAzureFunc();
        }
        const azureLangTrans = { "en-au": "Australia", "en-ca": "Canada", "en-gb": "United Kingdom", "en-gh": "Ghana", "en-hk": "Hong Kong SAR", "en-ie": "Ireland", "en-in": "India", "en-ke": "Kenya", "en-ng": "Nigeria", "en-nz": "New Zealand", "en-ph": "Philippines", "en-sg": "Singapore", "en-tz": "Tanzania", "en-us": "United States", "en-za": "South Africa", "nan-cn": "闽南语，简体", "wuu-cn": "吴语，简体", "yue-cn": "粤语，简体", "zh-cn": "普通话，简体", "zh-cn-anhui": "安徽江淮普通话，简体", "zh-cn-bilingual": "普通话，英语双语", "zh-cn-gansu": "甘肃兰银普通话，简体", "zh-cn-guangxi": "广西口音普通话，简体", "zh-cn-henan": "中原官话河南，简体", "zh-cn-hunan": "湖南口音普通话，简体", "zh-cn-liaoning": "东北官话，简体", "zh-cn-shaanxi": "中原官话陕西，简体", "zh-cn-shandong": "冀鲁官话，简体", "zh-cn-shanxi": "山西口音普通话，简体", "zh-cn-sichuan": "西南官话，简体", "zh-hk": "粤语，繁体", "zh-sg": "简体，新加坡", "zh-tw": "台湾普通话" };
        const initRecSetting = (azureData) => {
            let langs = [['中文'], ['English']];
            if (azureData) {
                azureData.forEach(item => {
                    if (item.startsWith("en-")) {
                        let lowCase = item.toLowerCase();
                        let dialectName = azureLangTrans[lowCase] || lowCase;
                        if (lowCase == "en-us") langs[1].splice(1, 0, [lowCase, dialectName]);
                        else langs[1].push([lowCase, dialectName]);
                    } else if (item.indexOf("CN") != -1 || item.indexOf("zh") != -1) {
                        let lowCase = item.toLowerCase();
                        let dialectName = azureLangTrans[lowCase] || lowCase;
                        if (lowCase == "zh-cn") langs[0].splice(1, 0, [lowCase, dialectName]);
                        else langs[0].push([lowCase, dialectName]);
                    }
                })
            } else if (existRec === 3) {
                langs = [
                    ['自动检测', ['', '自动检测']],
                    ['中文', ['zh', '汉语']],
                    ['English', ['en', 'English']]
                ];
            } else {
                langs = [ // from https://www.google.com/intl/en/chrome/demos/speech.html
                    ['中文', ['cmn-Hans-CN', '普通话 (大陆)'],
                        ['cmn-Hans-HK', '普通话 (香港)'],
                        ['cmn-Hant-TW', '中文 (台灣)'],
                        ['yue-Hant-HK', '粵語 (香港)']],
                    ['English', ['en-US', 'United States'],
                        ['en-GB', 'United Kingdom'],
                        ['en-AU', 'Australia'],
                        ['en-CA', 'Canada'],
                        ['en-IN', 'India'],
                        ['en-KE', 'Kenya'],
                        ['en-TZ', 'Tanzania'],
                        ['en-GH', 'Ghana'],
                        ['en-NZ', 'New Zealand'],
                        ['en-NG', 'Nigeria'],
                        ['en-ZA', 'South Africa'],
                        ['en-PH', 'Philippines']]
                ];
            };
            toggleRecCheck(false);
            if (locale !== "zh") {
                if (existRec === 3) {
                    langs[0][0] = langs[0][1][1] = translations[locale]["autoWord"];
                    let idx = langs.findIndex((item) => { return item[1][0] === locale });
                    let [temp] = langs.splice(idx, 1);
                    langs.splice(1, 0, temp);
                } else langs = langs.reverse();
            }
            selectLangOption.options.length = select_language.options.length = 0;
            langs.forEach((lang, i) => {
                select_language.options.add(new Option(lang[0], i));
                selectLangOption.options.add(new Option(lang[0], i))
            });
            const updateCountry = function () {
                selectLangOption.selectedIndex = select_language.selectedIndex = this.selectedIndex;
                select_dialect.innerHTML = "";
                selectDiaOption.innerHTML = "";
                let list = langs[select_language.selectedIndex];
                for (let i = 1; i < list.length; i++) {
                    select_dialect.options.add(new Option(list[i][1], list[i][0]));
                    selectDiaOption.options.add(new Option(list[i][1], list[i][0]));
                }
                select_dialect.style.visibility = list[1].length == 1 ? "hidden" : "visible";
                selectDiaOption.parentElement.style.visibility = list[1].length == 1 ? "hidden" : "visible";
                localStorage.setItem("voiceRecLang", select_dialect.value);
            };
            let localLangIdx = 0;
            let localDiaIdx = 0;
            let localRecLang = localStorage.getItem("voiceRecLang") || langs[0][1][0];
            if (localRecLang) {
                let localIndex = langs.findIndex(item => {
                    let diaIdx = item.findIndex(lang => { return lang instanceof Array && lang[0] === localRecLang });
                    if (diaIdx !== -1) {
                        localDiaIdx = diaIdx - 1;
                        return true;
                    }
                    return false;
                });
                if (localIndex !== -1) localLangIdx = localIndex;
            }
            selectLangOption.onchange = updateCountry;
            select_language.onchange = updateCountry;
            selectDiaOption.onchange = select_dialect.onchange = function () {
                selectDiaOption.selectedIndex = select_dialect.selectedIndex = this.selectedIndex;
                localStorage.setItem("voiceRecLang", select_dialect.value);
            }
            selectLangOption.selectedIndex = select_language.selectedIndex = localLangIdx;
            select_language.dispatchEvent(new Event("change"));
            selectDiaOption.selectedIndex = select_dialect.selectedIndex = localDiaIdx;
            select_dialect.dispatchEvent(new Event("change"));
            initRecEvent();
        };
        let recSetTimer;
        let initRecFunc = () => {
            if (!supportRec) return;
            let localAutoSendWord = localStorage.getItem("autoVoiceSendWord");
            autoSendWord = autoSendText.value = localAutoSendWord || autoSendText.getAttribute("value") || "";
            autoSendText.onchange = () => {
                autoSendWord = autoSendText.value;
                localStorage.setItem("autoVoiceSendWord", autoSendWord);
            }
            autoSendText.dispatchEvent(new Event("change"));
            let localAutoStopWord = localStorage.getItem("autoVoiceStopWord");
            autoStopWord = autoStopText.value = localAutoStopWord || autoStopText.getAttribute("value") || "";
            autoStopText.onchange = () => {
                autoStopWord = autoStopText.value;
                localStorage.setItem("autoVoiceStopWord", autoStopWord);
            }
            autoStopText.dispatchEvent(new Event("change"));
            let outEle = document.getElementById("autoSendTimeout");
            let localTimeout = localStorage.getItem("autoVoiceSendOut");
            outEle.value = autoSendTime = parseInt(localTimeout || outEle.getAttribute("value"));
            outEle.oninput = () => {
                outEle.style.backgroundSize = (outEle.value - outEle.min) * 100 / (outEle.max - outEle.min) + "% 100%";
                autoSendTime = parseInt(outEle.value);
                localStorage.setItem("autoVoiceSendOut", outEle.value);
            }
            outEle.dispatchEvent(new Event("input"));
            outEle.onchange = () => {
                let hasAutoTimer = !!autoSendTimer;
                clearAutoSendTimer();
                if (hasAutoTimer) setAutoTimer();
            }
            const keepMicEle = document.getElementById("keepListenMic");
            let localKeepMic = localStorage.getItem("keepListenMic");
            keepMicEle.checked = keepListenMic = (localKeepMic || keepMicEle.getAttribute("checked")) === "true";
            keepMicEle.onchange = () => {
                keepListenMic = keepMicEle.checked;
                localStorage.setItem("keepListenMic", keepListenMic);
            }
            keepMicEle.dispatchEvent(new Event("change"));
            const closeEvent = (ev) => {
                if (voiceRecSetEle.contains(ev.target)) return;
                if (!voiceRecSetEle.contains(ev.target)) {
                    voiceRecSetEle.style.display = "none";
                    document.removeEventListener("mousedown", closeEvent, true);
                    voiceRecEle.classList.remove("voiceLong");
                }
            }
            const longEvent = () => {
                voiceRecSetEle.style.display = "block";
                document.addEventListener("mousedown", closeEvent, true);
            }
            const voiceDownEvent = (ev) => {
                ev.preventDefault();
                let i = 0;
                voiceRecEle.classList.add("voiceLong");
                recSetTimer = setInterval(() => {
                    i += 1;
                    if (i >= 3) {
                        clearInterval(recSetTimer);
                        recSetTimer = void 0;
                        longEvent();
                    }
                }, 100)
            }
            const voiceUpEvent = (ev) => {
                ev.preventDefault();
                if (recSetTimer !== void 0) {
                    toggleRecEv();
                    clearInterval(recSetTimer);
                    recSetTimer = void 0;
                    voiceRecEle.classList.remove("voiceLong");
                }
            }
            voiceRecEle.onmouseup = voiceUpEvent;
            voiceRecEle.ontouchend = voiceUpEvent;
            voiceRecEle.onmousedown = voiceDownEvent;
            voiceRecEle.ontouchstart = voiceDownEvent;
        }
        initRecFunc();
        class RiffPcmEncoder {
            constructor(actualSampleRate, desiredSampleRate) {
                this.privActualSampleRate = actualSampleRate;
                this.privDesiredSampleRate = desiredSampleRate;
            }
            encode(actualAudioFrame) {
                const audioFrame = this.downSampleAudioFrame(actualAudioFrame, this.privActualSampleRate, this.privDesiredSampleRate);
                if (!audioFrame) return null;
                const audioLength = audioFrame.length * 2;
                const buffer = new ArrayBuffer(audioLength);
                const view = new DataView(buffer);
                this.floatTo16BitPCM(view, 0, audioFrame);
                return buffer;
            }
            floatTo16BitPCM(view, offset, input) {
                for (let i = 0; i < input.length; i++, offset += 2) {
                    const s = Math.max(-1, Math.min(1, input[i]));
                    view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }
            downSampleAudioFrame(srcFrame, srcRate, dstRate) {
                if (!srcFrame) return null;
                if (dstRate === srcRate || dstRate > srcRate) return srcFrame;
                const ratio = srcRate / dstRate;
                const dstLength = Math.round(srcFrame.length / ratio);
                const dstFrame = new Float32Array(dstLength);
                let srcOffset = 0;
                let dstOffset = 0;
                while (dstOffset < dstLength) {
                    const nextSrcOffset = Math.round((dstOffset + 1) * ratio);
                    let accum = 0;
                    let count = 0;
                    while (srcOffset < nextSrcOffset && srcOffset < srcFrame.length) {
                        accum += srcFrame[srcOffset++];
                        count++;
                    }
                    dstFrame[dstOffset++] = accum / count;
                }
                return dstFrame;
            }
        }
        let recSocket;
        class Recorder {
            constructor() {
                this.ready = false;
                this.connId = "";
                this.reqId = "";
                this.label = "";
                this.processScriptURL = "";
                this.forceStop = false;
                this.sampleRate = 16000;
                // avgBytesPerSec / 10, 0.1s
                this.bufferSize = this.sampleRate / 5;
                this.chunks = [];
                this.chunksByte = 0;
                // "Content-Type: audio/x-wav\r\n" + WAV header
                this.wavHeader = new Uint8Array([67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 97, 117, 100, 105, 111, 47, 120, 45, 119, 97, 118, 13, 10, 82, 73, 70, 70, 0, 0, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 1, 0, 128, 62, 0, 0, 0, 125, 0, 0, 2, 0, 16, 0, 100, 97, 116, 97, 0, 0, 0, 0])
            }
            initRecorder() {
                return new Promise((res) => {
                    // microsoft cognitive-services-speech-sdk-js
                    this.context = navigator.mediaDevices.getSupportedConstraints().sampleRate ? new AudioContext({ sampleRate: this.sampleRate }) : new AudioContext();
                    this.audioInput = this.context.createMediaStreamSource(this.stream);
                    if (this.context.audioWorklet) {
                        if (this.processScriptURL == "") {
                            const workletScript = `${RiffPcmEncoder.toString()}
                            class SP extends AudioWorkletProcessor {
                            constructor(options) {
                                super(options);
                                this.sampleRate = ${this.sampleRate};
                                // avgBytesPerSec / 10, 0.1s
                                this.bufferSize = ${this.bufferSize};
                                this.encoder = new RiffPcmEncoder(options.processorOptions.sampleRate, this.sampleRate);
                                this.chunks = [];
                                this.chunksByte = 0;
                                this.processing = true;
                                this.port.onmessage = (e) => {
                                if (e.data === "stop") {
                                    this.processing = false;
                                    this.port.close();
                                }
                                }
                            }
                            concat() {
                                let result = new Uint8Array(this.bufferSize);
                                let offset = 0;
                                for (let i = 0; i < this.chunks.length; i++) {
                                result.set(this.chunks[i], offset);
                                offset += this.chunks[i].byteLength;
                                }
                                return result;
                            }
                            process(inputs) {
                                if (inputs[0][0]) {
                                let data = new Uint8Array(this.encoder.encode(inputs[0][0]));
                                this.chunks.push(data);
                                this.chunksByte += data.byteLength;
                                if (this.chunksByte > this.bufferSize) {
                                    let lastChunk = this.chunks[this.chunks.length - 1];
                                    this.chunks[this.chunks.length - 1] = lastChunk.subarray(0, lastChunk.byteLength - this.chunksByte + this.bufferSize);
                                    let chunk = this.concat();
                                    this.port.postMessage(chunk, [chunk.buffer]);
                                    this.chunks.length = 0;
                                    this.chunks.push(lastChunk.subarray(lastChunk.byteLength - this.chunksByte + this.bufferSize));
                                    this.chunksByte = this.chunks[0].byteLength;
                                } else if (this.chunksByte === this.bufferSize) {
                                    let chunk = this.concat();
                                    this.port.postMessage(chunk, [chunk.buffer]);
                                    this.chunks.length = this.chunksByte = 0;
                                }
                                }
                                return this.processing;
                            }
                            }
                            registerProcessor('speech-processor', SP);`;
                            this.processScriptURL = URL.createObjectURL(new Blob([workletScript], { type: "application/javascript; charset=utf-8" }));
                        }
                        this.context.audioWorklet.addModule(this.processScriptURL).then(() => {
                            this.recorder = new AudioWorkletNode(this.context, "speech-processor", {
                                processorOptions: { sampleRate: this.context.sampleRate }
                            });
                            this.ready = true;
                            this.recorder.port.onmessage = (e) => { if (e.data && this.ready) recSocket.send(this.getRecBin(e.data)) };
                            if (isFirefox) { // tested firefox need volume gain
                                this.gain = this.context.createGain();
                                this.gain.gain.value = 3;
                                this.audioInput.connect(this.gain);
                                this.gain.connect(this.recorder);
                            } else this.audioInput.connect(this.recorder);
                            this.recorder.connect(this.context.destination);
                            res();
                        }).catch(e => { this.attachScriptProcessor(res) })
                    } else this.attachScriptProcessor(res);
                })
            }
            attachScriptProcessor(res) {
                this.encoder = new RiffPcmEncoder(this.context.sampleRate, this.sampleRate);
                this.recorder = (() => {
                    let bufferSize = 0;
                    try {
                        return this.context.createScriptProcessor(bufferSize, 1, 1);
                    } catch (error) {
                        // Webkit (<= version 31) requires a valid bufferSize.
                        bufferSize = 2048;
                        let audioSampleRate = this.context.sampleRate;
                        while (bufferSize < 16384 && audioSampleRate >= (this.audioInput.channelCount * this.sampleRate)) {
                            bufferSize <<= 1;
                            audioSampleRate >>= 1;
                        }
                        return this.context.createScriptProcessor(bufferSize, 1, 1);
                    }
                })();
                this.ready = true;
                this.recorder.onaudioprocess = (event) => {
                    const inputFrame = event.inputBuffer.getChannelData(0);
                    if (inputFrame && this.ready) this.pushWSFrame(new Uint8Array(this.encoder.encode(inputFrame)));
                };
                if (isFirefox) { // tested firefox need volume gain
                    this.gain = this.context.createGain();
                    this.gain.gain.value = 3;
                    this.audioInput.connect(this.gain);
                    this.gain.connect(this.recorder);
                } else this.audioInput.connect(this.recorder);
                this.recorder.connect(this.context.destination);
                res();
            }
            pushWSFrame(audio) {
                let totalByte = this.chunksByte + audio.byteLength;
                if (totalByte >= this.bufferSize) {
                    let offset = 0;
                    for (let i = 0; i < Math.floor(totalByte / this.bufferSize); i++) {
                        if (this.chunksByte) {
                            let partAudio = audio.subarray(offset, (i + 1) * this.bufferSize - this.chunksByte);
                            this.chunks.push(partAudio);
                            let chunk = this.concat();
                            recSocket.send(this.getRecBin(chunk));
                            offset = (i + 1) * this.bufferSize - this.chunksByte;
                            this.chunks.length = this.chunksByte = 0;
                        } else {
                            let partAudio = audio.subarray(offset, offset + this.bufferSize);
                            recSocket.send(this.getRecBin(partAudio));
                            offset += this.bufferSize;
                        }
                    }
                    if (offset < audio.byteLength) {
                        this.chunks.push(audio.subarray(offset));
                        this.chunksByte = this.chunks[0].byteLength;
                    }
                } else {
                    this.chunks.push(audio);
                    this.chunksByte += audio.byteLength;
                }
            }
            concat() {
                let result = new Uint8Array(this.bufferSize);
                let offset = 0;
                for (let i = 0; i < this.chunks.length; i++) {
                    result.set(this.chunks[i], offset);
                    offset += this.chunks[i].byteLength;
                }
                return result;
            }
            initRecWebsocket() {
                return new Promise((res, rej) => {
                    let url = `wss://${azureRecRegion}.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?Ocp-Apim-Subscription-Key=${azureRecKey}&language=${select_dialect.value}&storeAudio=true`;
                    if (!recSocket || recSocket.readyState > 1 || recSocket.url.slice(0, -48) !== url) {
                        if (recSocket && recSocket.readyState === 1) recSocket.close(1000);
                        recSocket = new WebSocket(url + `&X-ConnectionId=${this.connId}`);
                        recSocket.binaryType = "arraybuffer";
                        recSocket.onopen = () => { res() };
                        recSocket.onmessage = (e) => { this.handleWSMsg(e) };
                        recSocket.onerror = (e) => {
                            if (!this.ready) notyf.error(translations[locale]["badGateTip"]);
                            recSocket.close();
                            rej();
                        };
                        recSocket.onclose = (e) => {
                            if (this.ready) {
                                this.ready = false;
                                this.retryWebsocket(recSocket.url)
                            }
                        }
                    } else {
                        return res()
                    }
                })
            }
            retryWebsocket(url) {
                recSocket = new WebSocket(url);
                recSocket.binaryType = "arraybuffer";
                recSocket.onopen = () => {
                    this.startWSDetect();
                    this.ready = true;
                }
                recSocket.onmessage = (e) => { this.handleWSMsg(e) };
                recSocket.onerror = (e) => { recSocket.close() };
                recSocket.onclose = (e) => {
                    notyf.error(translations[locale]["badGateTip"]);
                    this.stopRecorder(true);
                }
            }
            handleWSMsg(e) {
                if (typeof e.data === "string") {
                    let path = e.data.match(/Path:(.+)/)[1].trim();
                    let splitData = e.data.split("\n");
                    if (this.ready && (path === "speech.phrase" || path === "speech.hypothesis")) {
                        let data = JSON.parse(splitData[splitData.length - 1]);
                        let isFinal = data.DisplayText !== void 0;
                        let autoFlag;
                        if (isFinal) {
                            recRes += data.DisplayText;
                            if (autoSendWord) {
                                let idx = recRes.indexOf(autoSendWord);
                                if (idx !== -1) {
                                    recRes = recRes.slice(0, idx);
                                    autoFlag = 1;
                                }
                            }
                            if (autoStopWord) {
                                let idx = recRes.indexOf(autoStopWord);
                                if (idx !== -1) {
                                    recRes = recRes.slice(0, idx);
                                    autoFlag = 2;
                                }
                            }
                        }
                        else if (data.Text) { tempRes = recRes + data.Text }
                        inputAreaEle.value = preRes + (isFinal ? recRes : tempRes) + affRes;
                        textInputEvent();
                        inputAreaEle.focus();
                        inputAreaEle.selectionEnd = inputAreaEle.value.length - affRes.length;
                        if (autoFlag) {
                            if (autoFlag === 1) genFunc();
                            else this.stopRecorder(true);
                        }
                        clearAutoSendTimer();
                        if (autoFlag !== 1) setAutoTimer();
                    } else if (path === "turn.end") {
                        if (!this.forceStop && keepListenMic || this.ready) {
                            this.startWSDetect();
                            this.ready = true;
                        }
                    }
                }
            }
            startWSDetect() {
                this.reqId = uuidv4(true);
                recSocket.send(this.getRecPre(this.label));
                recSocket.send(this.getRecConfig());
                recSocket.send(this.getRecPreBin());
            }
            getRecPre(label) {
                let osPlatform = (typeof window !== "undefined") ? "Browser" : "Node";
                osPlatform += "/" + navigator.platform;
                let osName = navigator.userAgent;
                let osVersion = navigator.appVersion;
                return `Path: speech.config\r\nX-RequestId: ${this.reqId}\r\nX-Timestamp: ${new Date().toISOString()}\r\nContent-Type: application/json\r\n\r\n{"context":{"system":{"name":"SpeechSDK","version":"1.35.0","build":"JavaScript","lang":"JavaScript"},"os":{"platform":"${osPlatform}","name":"${osName}","version":"${osVersion}"},"audio":{"source":{"bitspersample":16,"channelcount":1,"connectivity":"Unknown","manufacturer":"Speech SDK","model":"${label}","samplerate":${this.sampleRate},"type":"Microphones"}}},"recognition":"conversation"}`
            }
            getRecConfig() {
                return `Path: speech.context\r\nX-RequestId: ${this.reqId}\r\nX-Timestamp: ${new Date().toISOString()}\r\nContent-Type: application/json\r\n\r\n{"phraseDetection":{}}`
            }
            getRecPreBin() {
                let header = this.getRecHeader();
                let data = new Uint8Array(2 + header.length + this.wavHeader.byteLength);
                data.set([0, 126], 0);
                data.set(stringToArray(header), 2);
                data.set(this.wavHeader, 2 + header.length);
                return data
            }
            getRecBin(audio) {
                let header = this.getRecHeader();
                let data = new Uint8Array(2 + header.length + audio.byteLength);
                data.set([0, 99], 0);
                data.set(stringToArray(header), 2);
                data.set(audio, 2 + header.length);
                return data
            }
            getRecHeader() {
                return `Path: audio\r\nX-RequestId: ${this.reqId}\r\nX-Timestamp: ${new Date().toISOString()}\r\n`
            }
            getMedia() {
                return new Promise((res, rej) => {
                    navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
                        this.stream = stream;
                        res();
                    }).catch(e => {
                        notyf.error(translations[locale][e.name === "NotAllowedError" ? "noMicPerTip" : "noMicTip"]);
                        rej();
                    })
                })
            }
            async startRecorder() {
                return new Promise((res, rej) => {
                    this.connId = uuidv4(true);
                    Promise.all([this.getMedia(), this.initRecWebsocket()]).then((val) => {
                        this.label = this.stream.getAudioTracks()[0].label;
                        this.startWSDetect();
                        this.initRecorder().then(() => { res() });
                    }).catch(e => {
                        if (this.stream) {
                            this.stream.getAudioTracks().forEach(track => { track.stop() });
                            this.stream = null;
                        }
                        rej(e);
                    })
                })
            }
            stopRecWebsocket() {
                if (recSocket && recSocket.readyState === 1) {
                    let endBin = this.getRecBin(new Uint8Array());
                    recSocket.send(endBin);
                    recSocket.send(endBin);
                }
            }
            stopRecorder(forceStop) {
                this.forceStop = forceStop;
                this.ready = false;
                this.stopRecWebsocket();
                clearAutoSendTimer();
                if (!forceStop && keepListenMic) return;
                voiceRecEle.classList.remove("voiceRecing");
                recing = false;
                if (this.recorder && this.recorder.port) {
                    this.recorder.port.postMessage("stop");
                    this.recorder.port.close();
                }
                if (this.stream) {
                    this.stream.getAudioTracks().forEach(track => { track.stop() });
                    this.stream = null;
                }
                if (this.audioInput) {
                    this.audioInput.disconnect();
                    this.audioInput = null;
                }
                if (isFirefox && this.gain) {
                    this.gain.disconnect();
                    this.gain = null;
                }
                if (this.recorder) {
                    this.recorder.disconnect();
                    this.recorder = null;
                }
                if (this.context) {
                    this.context.close();
                    this.context = null;
                }
            }
        }
        class LegacyRecorder {
            constructor() {
                this.mimeType = MediaRecorder.isTypeSupported("audio/mp4") ? "audio/mp4" : "audio/webm";
                this.suffix = this.mimeType === "audio/mp4" ? ".mp4" : ".webm";
                this.bitsPerSecond = 128000;
                this.chunks = [];
            }
            initRecorder() {
                this.recorder = new MediaRecorder(this.stream, { mimeType: this.mimeType, audioBitsPerSecond: this.bitsPerSecond });
                this.chunks.length = 0;
                this.recorder.ondataavailable = e => { this.chunks.push(e.data) };
                this.recorder.start(1e3);
            }
            async processData(blob) {
                let formData = new FormData();
                formData.append("model", "whisper-1");
                formData.append("file", blob, "audio" + this.suffix);
                if (select_dialect.value !== "") formData.append("language", select_dialect.value);
                let url = apiHost + ((apiHost.length && !apiHost.endsWith("/")) ? "/" : "") + "v1/audio/transcriptions";
                let controller = new AbortController();
                let controllerId = setTimeout(() => {
                    notyf.error(translations[locale]["timeoutTip"]);
                    controller.abort();
                }, 15000);
                try {
                    const res = await fetch(url, {
                        method: "POST",
                        body: formData,
                        signal: controller.signal,
                        ...(customAPIKey ? { headers: { Authorization: "Bearer " + customAPIKey } } : {})
                    });
                    clearTimeout(controllerId);
                    if (res.status === 200) {
                        let result = await res.json();
                        if (result && result.text) {
                            inputAreaEle.value = preRes + result.text + affRes;
                            textInputEvent();
                            inputAreaEle.focus();
                            inputAreaEle.selectionEnd = inputAreaEle.value.length - affRes.length;
                            return true;
                        }
                    } else notyf.open({ type: "warning", message: translations[locale]["cantTranscribeTip"] });
                } catch (e) { }
            }
            getMedia() {
                return new Promise((res, rej) => {
                    navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
                        this.stream = stream;
                        res();
                    }).catch(e => {
                        notyf.error(translations[locale][e.name === "NotAllowedError" ? "noMicPerTip" : "noMicTip"]);
                        rej(e);
                    })
                })
            }
            async startRecorder() {
                return new Promise((res, rej) => {
                    this.getMedia().then(() => {
                        this.initRecorder();
                        res();
                    }).catch(e => { rej(e) })
                })
            }
            stopRecorder(forceStop) {
                return new Promise((res, rej) => {
                    clearAutoSendTimer();
                    if (this.recorder && this.recorder.state === "recording") {
                        this.recorder.onstop = async (e) => {
                            let blob = new Blob(this.chunks, { type: this.mimeType });
                            this.chunks.length = 0;
                            try {
                                let result = await this.processData(blob);
                                if (result) res();
                                else rej();
                            } catch (error) { rej() };
                            if (!forceStop && keepListenMic) this.recorder.start(1e3);
                        };
                        this.recorder.stop();
                    } else res();
                    if (!forceStop && keepListenMic) return;
                    voiceRecEle.classList.remove("voiceRecing");
                    recing = false;
                    if (this.recorder) this.recorder = null;
                    if (this.stream) {
                        this.stream.getAudioTracks().forEach(track => { track.stop() });
                        this.stream = null;
                    }
                })
            }
        }
        let recorder, legacyRecorder, initingRecorder;
        let recRes = tempRes = "";
        let preRes, affRes;
        resetRecRes = () => {
            preRes = inputAreaEle.value.slice(0, inputAreaEle.selectionStart);
            affRes = inputAreaEle.value.slice(inputAreaEle.selectionEnd);
            recRes = tempRes = "";
        }
        const initRecEvent = () => {
            if (existRec === 3) {
                if (legacyRecorder === void 0) legacyRecorder = new LegacyRecorder();
                toggleRecEv = async (force = true) => {
                    if (voiceRecEle.classList.contains("voiceRecing")) await legacyRecorder.stopRecorder(force);
                    else {
                        if (initingRecorder) return;
                        resetRecRes();
                        initingRecorder = true;
                        legacyRecorder.startRecorder().then(() => {
                            recing = true;
                            initingRecorder = false;
                            voiceRecEle.classList.add("voiceRecing");
                        }).catch(e => {
                            legacyRecorder.stopRecorder(force);
                            initingRecorder = false;
                        })
                    }
                }
            } else if (existRec === 2) {
                if (recorder === void 0) recorder = new Recorder();
                toggleRecEv = (force = true) => {
                    if (voiceRecEle.classList.contains("voiceRecing")) recorder.stopRecorder(force);
                    else {
                        if (initingRecorder) return;
                        resetRecRes();
                        initingRecorder = true;
                        recorder.startRecorder().then(() => {
                            recing = true;
                            initingRecorder = false;
                            voiceRecEle.classList.add("voiceRecing");
                        }).catch(e => {
                            recorder.stopRecorder(force)
                            initingRecorder = false;
                        })
                    }
                }
            } else {
                let recIns = new webkitSpeechRecognition();
                // prevent some Android bug
                recIns.continuous = !isAndroid;
                recIns.interimResults = true;
                recIns.maxAlternatives = 1;
                let resEvent = (event) => {
                    if (typeof (event.results) === "undefined") {
                        toggleRecEv();
                        return;
                    }
                    let isFinal;
                    let autoFlag;
                    for (let i = event.resultIndex; i < event.results.length; ++i) {
                        isFinal = event.results[i].isFinal;
                        if (isFinal) {
                            recRes += event.results[i][0].transcript
                            if (autoSendWord) {
                                let idx = recRes.indexOf(autoSendWord);
                                if (idx !== -1) {
                                    recRes = recRes.slice(0, idx);
                                    autoFlag = 1;
                                    break;
                                }
                            }
                            if (autoStopWord) {
                                let idx = recRes.indexOf(autoStopWord);
                                if (idx !== -1) {
                                    recRes = recRes.slice(0, idx);
                                    autoFlag = 2;
                                    break;
                                }
                            }
                        }
                        else { tempRes = recRes + event.results[i][0].transcript }
                    }
                    inputAreaEle.value = preRes + (isFinal ? recRes : tempRes) + affRes;
                    textInputEvent();
                    inputAreaEle.focus();
                    inputAreaEle.selectionEnd = inputAreaEle.value.length - affRes.length;
                    if (autoFlag) {
                        if (autoFlag === 1) genFunc();
                        else endEvent(false, false);
                    }
                    clearAutoSendTimer();
                    if (autoFlag !== 1) setAutoTimer();
                };
                const stopAction = () => {
                    clearAutoSendTimer();
                    recIns.onresult = null;
                    recIns.onerror = null;
                    recIns.onend = null;
                    voiceRecEle.classList.remove("voiceRecing");
                    recing = false;
                };
                const endEvent = (event, flag) => {
                    if (flag !== void 0) {
                        if (!flag) {
                            recIns.stop();
                            stopAction();
                        }
                    } else if (event) {
                        if (keepListenMic && event.type === "end") {
                            recIns.start();
                            resetRecRes();
                        } else {
                            if (event.type === "error") recIns.stop();
                            stopAction();
                        }
                    }
                };
                const errorEvent = (ev) => {
                    if (event.error === "no-speech") {
                        notyf.open({
                            type: "warning",
                            message: translations[locale]["noSpeechTip"]
                        });
                    }
                    if (event.error === "audio-capture") {
                        notyf.error(translations[locale]["noMicTip"])
                        endEvent(ev);
                    }
                    if (event.error === "not-allowed") {
                        notyf.error(translations[locale]["noMicPerTip"])
                        endEvent(ev);
                    }
                };
                toggleRecEv = () => {
                    if (voiceRecEle.classList.toggle("voiceRecing")) {
                        try {
                            resetRecRes();
                            recIns.lang = select_dialect.value;
                            recIns.start();
                            recIns.onresult = resEvent;
                            recIns.onerror = errorEvent;
                            recIns.onend = endEvent;
                            recing = true;
                        } catch (e) {
                            endEvent(false, false);
                        }
                    } else {
                        endEvent(false, false);
                    }
                };
            }
        }
        recServiceEle.dispatchEvent(new Event("change"));
        document.querySelector(".modelSwitch").onclick = document.querySelector(".sysSwitch").onclick = document.querySelector(".setSwitch").onclick = function (ev) {
            let activeEle = this.getElementsByClassName("activeSwitch")[0];
            if (ev.target !== activeEle) {
                activeEle.classList.remove("activeSwitch");
                ev.target.classList.add("activeSwitch");
                document.getElementById(ev.target.dataset.id).style.display = "block";
                document.getElementById(activeEle.dataset.id).style.display = "none";
            }
        };
        if (!supportSpe) speechServiceEle.remove(3);
        const initVoiceVal = () => {
            let localVoiceType = localStorage.getItem("existVoice");
            speechServiceEle.value = existVoice = parseInt(localVoiceType || "2");
        }
        initVoiceVal();
        const clearAzureVoice = () => {
            azureKey = void 0;
            localStorage.removeItem(azureRegion + "VoiceData");
            azureRegion = void 0;
            azureRole = [];
            azureStyle = [];
            document.getElementById("azureExtra").style.display = "none";
            azureKeyInput.parentElement.style.display = "none";
            preSetAzureRegion.parentElement.style.display = "none";
        }
        speechServiceEle.onchange = () => {
            existVoice = parseInt(speechServiceEle.value);
            localStorage.setItem("existVoice", existVoice);
            toggleVoiceCheck(true);
            if (checkAzureAbort && !checkAzureAbort.signal.aborted) {
                checkAzureAbort.abort();
                checkAzureAbort = void 0;
            }
            if (checkEdgeAbort && !checkEdgeAbort.signal.aborted) {
                checkEdgeAbort.abort();
                checkEdgeAbort = void 0;
            }
            if (existVoice === 4) {
                toggleVoiceCheck(false);
                clearAzureVoice();
                loadOpenAIVoice();
            } else if (existVoice === 3) {
                azureKeyInput.parentElement.style.display = "block";
                preSetAzureRegion.parentElement.style.display = "block";
                loadAzureVoice();
            } else if (existVoice === 2) {
                clearAzureVoice();
                loadEdgeVoice();
            } else if (existVoice === 1) {
                toggleVoiceCheck(false);
                clearAzureVoice();
                loadLocalVoice();
            }
        }
        let openaiVoiceData, edgeVoiceData, systemVoiceData, checkAzureAbort, checkEdgeAbort;
        const toggleVoiceCheck = (bool) => {
            checkVoiceLoad.style.display = bool ? "flex" : "none";
            speechDetail.style.display = bool ? "none" : "block";
        }
        const loadOpenAIVoice = () => {
            if (openaiVoiceData) {
                initVoiceSetting(openaiVoiceData);
            } else {
                openaiVoiceData = [{ name: "alloy", displayName: "alloy" }, { name: "ash", displayName: "ash" }, { name: "coral", displayName: "coral" }, { name: "echo", displayName: "echo" }, { name: "fable", displayName: "fable" }, { name: "onyx", displayName: "onyx" }, { name: "nova", displayName: "nova" }, { name: "sage", displayName: "sage" }, { name: "shimmer", displayName: "shimmer" }]
                initVoiceSetting(openaiVoiceData);
            }
        };
        const loadAzureVoice = () => {
            let checking = false;
            const checkAzureFunc = () => {
                if (checking) return;
                if (azureKey) {
                    checking = true;
                    checkVoiceLoad.classList.add("voiceChecking");
                    checkAzureAbort = new AbortController();
                    setTimeout(() => {
                        if (checkAzureAbort && !checkAzureAbort.signal.aborted) {
                            checkAzureAbort.abort();
                            checkAzureAbort = void 0;
                        }
                    }, 15000);
                    getAzureToken(checkAzureAbort.signal).then(() => {
                        getVoiceList(checkAzureAbort.signal).then(() => {
                            toggleVoiceCheck(false);
                        }).catch(e => {
                        }).finally(() => {
                            checkVoiceLoad.classList.remove("voiceChecking");
                            checking = false;
                        })
                    }).catch(e => {
                    }).finally(() => {
                        checkVoiceLoad.classList.remove("voiceChecking");
                        checking = false;
                    })
                }
            };
            checkVoiceLoad.onclick = checkAzureFunc;
            const getAzureToken = (signal) => {
                return new Promise((res, rej) => {
                    fetch("https://" + azureRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken", {
                        signal,
                        method: "POST",
                        headers: {
                            "Ocp-Apim-Subscription-Key": azureKey
                        }
                    }).then(response => {
                        response.text().then(text => {
                            try {
                                let json = JSON.parse(text);
                                notyf.error(translations[locale]["azureInvalidTip"]);
                                rej();
                            } catch (e) {
                                res();
                            }
                        });
                    }).catch(e => {
                        localStorage.removeItem(azureRegion + "VoiceData");
                        rej();
                    })
                })
            };
            const getVoiceList = (signal) => {
                return new Promise((res, rej) => {
                    let localAzureVoiceData = localStorage.getItem(azureRegion + "VoiceData");
                    if (localAzureVoiceData) {
                        initVoiceSetting(JSON.parse(localAzureVoiceData));
                        res();
                    } else {
                        fetch("https://" + azureRegion + ".tts.speech.microsoft.com/cognitiveservices/voices/list", {
                            signal,
                            headers: {
                                "Ocp-Apim-Subscription-Key": azureKey
                            }
                        }).then(response => {
                            response.json().then(json => {
                                localStorage.setItem(azureRegion + "VoiceData", JSON.stringify(json));
                                initVoiceSetting(json);
                                res();
                            }).catch(e => {
                                notyf.error(translations[locale]["azureInvalidTip"]);
                                rej();
                            })
                        }).catch(e => {
                            localStorage.removeItem(azureRegion + "VoiceData");
                            rej();
                        })
                    }
                })
            };
            let azureRegionEle = document.getElementById("preSetAzureRegion");
            if (!azureRegionEle.options.length) {
                azureRegions.forEach((region, i) => {
                    let option = document.createElement("option");
                    option.value = region;
                    option.text = region;
                    azureRegionEle.options.add(option);
                });
            }
            let localAzureRegion = localStorage.getItem("azureRegion");
            if (localAzureRegion) {
                azureRegion = localAzureRegion;
                azureRegionEle.value = localAzureRegion;
            }
            azureRegionEle.onchange = () => {
                azureRegion = azureRegionEle.value;
                localStorage.setItem("azureRegion", azureRegion);
                toggleVoiceCheck(true);
            }
            azureRegionEle.dispatchEvent(new Event("change"));
            let azureKeyEle = document.getElementById("azureKeyInput");
            let localAzureKey = localStorage.getItem("azureKey");
            if (localAzureKey) {
                azureKey = localAzureKey;
                azureKeyEle.value = localAzureKey;
            }
            azureKeyEle.onchange = () => {
                azureKey = azureKeyEle.value;
                localStorage.setItem("azureKey", azureKey);
                toggleVoiceCheck(true);
            }
            azureKeyEle.dispatchEvent(new Event("change"));
            if (azureKey) {
                checkAzureFunc();
            }
        }
        const loadEdgeVoice = () => {
            let checking = false;
            const endCheck = () => {
                checkVoiceLoad.classList.remove("voiceChecking");
                checking = false;
            };
            const checkEdgeFunc = () => {
                if (checking) return;
                checking = true;
                checkVoiceLoad.classList.add("voiceChecking");
                if (edgeVoiceData) {
                    initVoiceSetting(edgeVoiceData);
                    toggleVoiceCheck(false);
                    endCheck();
                } else {
                    checkEdgeAbort = new AbortController();
                    setTimeout(() => {
                        if (checkEdgeAbort && !checkEdgeAbort.signal.aborted) {
                            checkEdgeAbort.abort();
                            checkEdgeAbort = void 0;
                        }
                    }, 10000);
                    fetch("https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4", { signal: checkEdgeAbort.signal }).then(response => {
                        response.json().then(json => {
                            edgeVoiceData = json;
                            toggleVoiceCheck(false);
                            initVoiceSetting(json);
                            endCheck();
                        });
                    }).catch(err => {
                        endCheck();
                    })
                }
            };
            checkEdgeFunc();
            checkVoiceLoad.onclick = checkEdgeFunc;
        };
        const loadLocalVoice = () => {
            if (systemVoiceData) {
                initVoiceSetting(systemVoiceData);
            } else {
                let initedVoice = false;
                const getLocalVoice = () => {
                    let voices = speechSynthesis.getVoices();
                    if (voices.length) {
                        if (!initedVoice) {
                            initedVoice = true;
                            systemVoiceData = voices;
                            initVoiceSetting(voices);
                        }
                        return true;
                    } else {
                        return false;
                    }
                }
                let syncExist = getLocalVoice();
                if (!syncExist) {
                    if ("onvoiceschanged" in speechSynthesis) {
                        speechSynthesis.onvoiceschanged = () => {
                            getLocalVoice();
                        }
                    } else if (speechSynthesis.addEventListener) {
                        speechSynthesis.addEventListener("voiceschanged", () => {
                            getLocalVoice();
                        })
                    }
                    let timeout = 0;
                    let timer = setInterval(() => {
                        if (getLocalVoice() || timeout > 1000) {
                            if (timeout > 1000) {
                                existVoice = 0;
                            }
                            clearInterval(timer);
                            timer = null;
                        }
                        timeout += 300;
                    }, 300)
                }
            }
        };
        const voicesEle = document.getElementById("preSetSpeech");
        const initVoiceSetting = (voices) => {
            if (existVoice < 4) {
                let isOnline = existVoice === 2 || existVoice === 3;
                // 支持中文和英文
                voices = isOnline ? voices.filter(item => item.Locale.match(/^(zh-|en-)/)) : voices.filter(item => item.lang.match(/^(zh-|en-)/));
                if (isOnline) {
                    voices.map(item => {
                        item.name = item.FriendlyName || (`${item.DisplayName} Online (${item.VoiceType}) - ${item.LocaleName}`);
                        item.lang = item.Locale;
                    })
                } else if (isSafari && voices[0].voiceURI.startsWith("com.apple")) {
                    voices = voices.filter(item => {
                        return !item.voiceURI.startsWith("com.apple.voice.super-compact")
                    })
                }
                voices.sort((a, b) => {
                    if (a.lang.slice(0, 2) === b.lang.slice(0, 2)) {
                        if (a.lang.slice(0, 2) === "zh") {
                            return (a.lang === b.lang) ? 0 : (a.lang > b.lang) ? 1 : -1; // zh-CN 在前
                        } else {
                            return 0
                        }
                    }
                    return (locale === "zh" ? (a.lang < b.lang) : (a.lang > b.lang)) ? 1 : -1; // 中文UI，则中文"z"在前
                });
                voices.map(item => {
                    if (item.name.match(/^(Google |Microsoft )/)) {
                        item.displayName = item.name.replace(/^.*? /, "");
                    } else {
                        item.displayName = item.name;
                    }
                });
                if (isSafari && !isOnline) {
                    voices.map(item => { item.displayName = `${item.name} (${item.lang})` });
                };
            };
            voicesData = voices;
            voicesEle.innerHTML = "";
            voices.forEach((voice, i) => {
                let option = document.createElement("option");
                option.value = i;
                option.text = voice.displayName;
                voicesEle.options.add(option);
            });
            const loadAnother = (type) => {
                type = type ^ 1;
                let localVoice = localStorage.getItem("voice" + type);
                if (localVoice) {
                    let localIndex = voices.findIndex(item => { return item.name === localVoice });
                    if (localIndex === -1) localIndex = 0;
                    voiceRole[type] = voices[localIndex];
                } else {
                    voiceRole[type] = voices[0];
                }
                if (existVoice === 3) {
                    let localStyle = localStorage.getItem("azureStyle" + type);
                    azureStyle[type] = localStyle ? localStyle : void 0;
                    let localRole = localStorage.getItem("azureRole" + type);
                    azureRole[type] = localRole ? localRole : void 0;
                }
            }
            voiceChange();
            loadAnother(voiceType);
        };
        let voiceChange;
        const initVoiceFunc = () => {
            voicesEle.onchange = () => {
                voiceRole[voiceType] = voicesData[voicesEle.value];
                localStorage.setItem("voice" + voiceType, voiceRole[voiceType].name);
                if (voiceRole[voiceType].StyleList || voiceRole[voiceType].RolePlayList) {
                    document.getElementById("azureExtra").style.display = "block";
                    let voiceStyles = voiceRole[voiceType].StyleList;
                    let voiceRoles = voiceRole[voiceType].RolePlayList;
                    if (voiceRoles) {
                        preSetVoiceRole.innerHTML = "";
                        let option = document.createElement("option");
                        option.value = "Default";
                        option.text = "Default";
                        preSetVoiceRole.options.add(option);
                        voiceRoles.forEach((role, i) => {
                            let option = document.createElement("option");
                            option.value = role;
                            option.text = role;
                            preSetVoiceRole.options.add(option);
                        });
                        let localRole = localStorage.getItem("azureRole" + voiceType);
                        if (localRole && voiceRoles.indexOf(localRole) !== -1) {
                            preSetVoiceRole.value = localRole;
                            azureRole[voiceType] = localRole;
                        } else {
                            preSetVoiceRole.selectedIndex = 0;
                            azureRole[voiceType] = voiceRole[0];
                        }
                        preSetVoiceRole.onchange = () => {
                            azureRole[voiceType] = preSetVoiceRole.value;
                            localStorage.setItem("azureRole" + voiceType, preSetVoiceRole.value);
                        }
                        preSetVoiceRole.dispatchEvent(new Event("change"));
                    } else {
                        azureRole[voiceType] = void 0;
                        localStorage.removeItem("azureRole" + voiceType);
                    }
                    preSetVoiceRole.style.display = voiceRoles ? "block" : "none";
                    preSetVoiceRole.previousElementSibling.style.display = voiceRoles ? "block" : "none";
                    if (voiceStyles) {
                        preSetVoiceStyle.innerHTML = "";
                        let option = document.createElement("option");
                        option.value = "Default";
                        option.text = "Default";
                        preSetVoiceStyle.options.add(option);
                        voiceStyles.forEach((style, i) => {
                            let option = document.createElement("option");
                            option.value = style;
                            option.text = style;
                            preSetVoiceStyle.options.add(option);
                        });
                        let localStyle = localStorage.getItem("azureStyle" + voiceType);
                        if (localStyle && voiceStyles.indexOf(localStyle) !== -1) {
                            preSetVoiceStyle.value = localStyle;
                            azureStyle[voiceType] = localStyle;
                        } else {
                            preSetVoiceStyle.selectedIndex = 0;
                            azureStyle[voiceType] = voiceStyles[0];
                        }
                        preSetVoiceStyle.onchange = () => {
                            azureStyle[voiceType] = preSetVoiceStyle.value;
                            localStorage.setItem("azureStyle" + voiceType, preSetVoiceStyle.value)
                        }
                        preSetVoiceStyle.dispatchEvent(new Event("change"));
                    } else {
                        azureStyle[voiceType] = void 0;
                        localStorage.removeItem("azureStyle" + voiceType);
                    }
                    preSetVoiceStyle.style.display = voiceStyles ? "block" : "none";
                    preSetVoiceStyle.previousElementSibling.style.display = voiceStyles ? "block" : "none";
                } else {
                    document.getElementById("azureExtra").style.display = "none";
                    azureRole[voiceType] = void 0;
                    localStorage.removeItem("azureRole" + voiceType);
                    azureStyle[voiceType] = void 0;
                    localStorage.removeItem("azureStyle" + voiceType);
                }
            };
            voiceChange = () => {
                let localVoice = localStorage.getItem("voice" + voiceType);
                if (localVoice) {
                    let localIndex = voicesData.findIndex(item => { return item.name === localVoice });
                    if (localIndex === -1) localIndex = 0;
                    voiceRole[voiceType] = voicesData[localIndex];
                    voicesEle.value = localIndex;
                } else {
                    voiceRole[voiceType] = voicesData[0];
                }
                voicesEle.dispatchEvent(new Event("change"));
            }
            let volumeEle = document.getElementById("voiceVolume");
            let localVolume = localStorage.getItem("voiceVolume0");
            voiceVolume[0] = parseFloat(localVolume || volumeEle.getAttribute("value"));
            const voiceVolumeChange = () => {
                let localVolume = localStorage.getItem("voiceVolume" + voiceType);
                volumeEle.value = voiceVolume[voiceType] = parseFloat(localVolume || volumeEle.getAttribute("value"));
                volumeEle.style.backgroundSize = (volumeEle.value - volumeEle.min) * 100 / (volumeEle.max - volumeEle.min) + "% 100%";
            }
            volumeEle.oninput = () => {
                volumeEle.style.backgroundSize = (volumeEle.value - volumeEle.min) * 100 / (volumeEle.max - volumeEle.min) + "% 100%";
                voiceVolume[voiceType] = parseFloat(volumeEle.value);
                localStorage.setItem("voiceVolume" + voiceType, volumeEle.value);
            }
            voiceVolumeChange();
            let rateEle = document.getElementById("voiceRate");
            let localRate = localStorage.getItem("voiceRate0");
            voiceRate[0] = parseFloat(localRate || rateEle.getAttribute("value"));
            const voiceRateChange = () => {
                let localRate = localStorage.getItem("voiceRate" + voiceType);
                rateEle.value = voiceRate[voiceType] = parseFloat(localRate || rateEle.getAttribute("value"));
                rateEle.style.backgroundSize = (rateEle.value - rateEle.min) * 100 / (rateEle.max - rateEle.min) + "% 100%";
            }
            rateEle.oninput = () => {
                rateEle.style.backgroundSize = (rateEle.value - rateEle.min) * 100 / (rateEle.max - rateEle.min) + "% 100%";
                voiceRate[voiceType] = parseFloat(rateEle.value);
                localStorage.setItem("voiceRate" + voiceType, rateEle.value);
            }
            voiceRateChange();
            let pitchEle = document.getElementById("voicePitch");
            let localPitch = localStorage.getItem("voicePitch0");
            voicePitch[0] = parseFloat(localPitch || pitchEle.getAttribute("value"));
            const voicePitchChange = () => {
                let localPitch = localStorage.getItem("voicePitch" + voiceType);
                pitchEle.value = voicePitch[voiceType] = parseFloat(localPitch || pitchEle.getAttribute("value"));
                pitchEle.style.backgroundSize = (pitchEle.value - pitchEle.min) * 100 / (pitchEle.max - pitchEle.min) + "% 100%";
            }
            pitchEle.oninput = () => {
                pitchEle.style.backgroundSize = (pitchEle.value - pitchEle.min) * 100 / (pitchEle.max - pitchEle.min) + "% 100%";
                voicePitch[voiceType] = parseFloat(pitchEle.value);
                localStorage.setItem("voicePitch" + voiceType, pitchEle.value);
            }
            voicePitchChange();
            document.getElementById("voiceTypes").onclick = (ev) => {
                let type = ev.target.dataset.type;
                if (type !== void 0) {
                    type = parseInt(type);
                    if (type != voiceType) {
                        voiceType = type;
                        ev.target.classList.add("selVoiceType");
                        ev.target.parentElement.children[type ^ 1].classList.remove("selVoiceType");
                        voiceChange();
                        voiceVolumeChange();
                        voiceRateChange();
                        voicePitchChange();
                    }
                };
            };
            const voiceTestEle = document.getElementById("testVoiceText");
            let localTestVoice = localStorage.getItem("voiceTestText");
            voiceTestText = voiceTestEle.value = localTestVoice || voiceTestEle.getAttribute("value");
            voiceTestEle.oninput = () => {
                voiceTestText = voiceTestEle.value;
                localStorage.setItem("voiceTestText", voiceTestText);
            }
            const contVoiceEle = document.getElementById("enableContVoice");
            let localCont = localStorage.getItem("enableContVoice");
            contVoiceEle.checked = enableContVoice = (localCont || contVoiceEle.getAttribute("checked")) === "true";
            contVoiceEle.onchange = () => {
                enableContVoice = contVoiceEle.checked;
                localStorage.setItem("enableContVoice", enableContVoice);
            }
            contVoiceEle.dispatchEvent(new Event("change"));
            const autoVoiceEle = document.getElementById("enableAutoVoice");
            let localAuto = localStorage.getItem("enableAutoVoice");
            autoVoiceEle.checked = enableAutoVoice = (localAuto || autoVoiceEle.getAttribute("checked")) === "true";
            autoVoiceEle.onchange = () => {
                enableAutoVoice = autoVoiceEle.checked;
                localStorage.setItem("enableAutoVoice", enableAutoVoice);
            }
            autoVoiceEle.dispatchEvent(new Event("change"));
        };
        initVoiceFunc();
        speechServiceEle.dispatchEvent(new Event("change"));
    </script>
    <script crossorigin="anonymous"
        src="https://fastly.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
    <script crossorigin="anonymous"
        src="https://fastly.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
    <script crossorigin="anonymous" src="https://fastly.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
    <script>
        const API_URL = "v1/chat/completions";
        const DeepSeek_API_URL = "chat/completions";
        const Gemini_API_URL = "v1/models/";
        const Claude_API_URL = "v1/messages";
        let loading = false;
        let presetRoleData = {
            "default": translations[locale]["defaultText"],
            "normal": translations[locale]["assistantText"],
            "cat": translations[locale]["catText"],
            "emoji": translations[locale]["emojiText"],
            "image": translations[locale]["imageText"]
        };
        let modelVersion; // 模型版本
        let modelType; // 1:ChatGPT，2:DeepSeek， 3，Azure，4:Gemini，5:Claude
        let apiHost; // api反代地址
        let deepseekApiHost;
        let azureAIApiHost;
        let geminiApiHost;
        let claudeApiHost;
        let apiSelects = []; // api地址列表
        let deepseekApiSelects = [];
        let azureAIApiSelects = [];
        let geminiApiSelects = [];
        let claudeApiSelects = [];
        let customAPIKey; // 自定义apiKey
        let deepseekAPIKey;
        let azureAIAPIKey;
        let geminiAPIKey;
        let claudeAPIKey;
        let customAPIModel; // 自定义apiModel
        let deepseekAPIModel;
        let azureAIAPIModel;
        let geminiAPIModel;
        let claudeAPIModel;
        let systemRole; // 自定义系统角色
        let roleNature; // 角色性格
        let roleTemp; // 回答质量
        let convWidth = []; // 会话宽度，0:窗口宽度，1:全屏宽度
        let textSpeed; // 打字机速度，越小越快
        let contLen; // 连续会话长度，默认25，对话包含25条上下文信息。设置为0即关闭连续会话
        let enableCOT; // 是否输出思维链，默认开启。
        let enableLongReply; // 是否开启长回复，默认关闭，开启可能导致api费用增加。
        let longReplyFlag;
        let voiceIns; // Audio or SpeechSynthesisUtterance
        const isFirefox = !!navigator.userAgent.match(/firefox/i);
        const supportMSE = !!window.MediaSource && !isFirefox; // 是否支持MSE（除了ios应该都支持）
        const voiceMIME = "audio/mpeg";
        const voiceFormat = "audio-24khz-48kbitrate-mono-mp3";
        const voicePreLen = 130;
        const voiceSuffix = ".mp3";
        const openAIVoiceSuffix = ".aac";
        let userAvatar; // 用户头像
        let customDarkOut;
        let isCaseSearch; // 搜索是否区分大小写
        let controller;
        let controllerId;
        const findOffsetTop = (ele, target) => { // target is positioned ancestor element
            if (ele.offsetParent !== target) return ele.offsetTop + findOffsetTop(ele.offsetParent, target);
            else return ele.offsetTop;
        }
        const findResEle = (ele) => {
            if (!ele.classList.contains("response")) return findResEle(ele.parentElement);
            else return ele;
        }
        const isContentBottom = (ele) => {
            if (refreshIdx !== void 0) {
                return currentResEle.clientHeight + currentResEle.offsetTop > messagesEle.scrollTop + messagesEle.clientHeight
            } else {
                return messagesEle.scrollHeight - messagesEle.scrollTop - messagesEle.clientHeight < 128;
            }
        }
        const isEleBottom = (ele) => {
            return ele.clientHeight + findOffsetTop(ele, messagesEle) > messagesEle.scrollTop + messagesEle.clientHeight;
        }
        const outOfMsgWindow = (ele) => {
            return ele.offsetTop > messagesEle.scrollTop + messagesEle.clientHeight || ele.offsetTop + ele.clientHeight < messagesEle.scrollTop
        }
        const scrollToBottom = () => {
            if (isContentBottom()) {
                if (refreshIdx !== void 0) {
                    messagesEle.scrollTo(0, currentResEle.clientHeight + currentResEle.offsetTop - messagesEle.clientHeight + 10)
                } else {
                    messagesEle.scrollTo(0, messagesEle.scrollHeight)
                }
            }
        }
        const scrollToBottomLoad = (ele) => {
            if (!controller || !ele.offsetParent) return;
            if (isEleBottom(ele)) {
                let resEle = findResEle(ele)
                messagesEle.scrollTo(0, resEle.clientHeight + resEle.offsetTop - messagesEle.clientHeight + 10)
            }
        }
        const forceRepaint = (ele) => {
            ele.style.display = "none";
            ele.offsetHeight;
            ele.style.display = null;
        }
        const parser = new DOMParser();
        const getUnescape = html => {
            return parser.parseFromString(html, 'text/html').body.textContent;
        }
        const escapeRegexExp = (str) => { // from vscode src/vs/base/common/strings.ts escapeRegExpCharacters
            return str.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g, '\\$&');
        }
        const checkStorage = () => {
            let used = 0;
            for (let key in localStorage) {
                localStorage.hasOwnProperty(key) && (used += localStorage[key].length)
            }
            let remain = 5242880 - used;
            usedStorageBar.style.width = (used / 5242880 * 100).toFixed(2) + "%";
            let usedMBs = used / 1048576;
            usedStorage.textContent = (usedMBs < 1 ? usedMBs.toPrecision(2) : usedMBs.toFixed(2)) + "MB";
            availableStorage.textContent = Math.floor(remain / 1048576 * 100) / 100 + "MB";
        };
        const UNESCAPE_RE = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;
        const superscript = (state, silent) => {
            let found,
                content,
                token,
                max = state.posMax,
                start = state.pos;
            if (state.src.charCodeAt(start) !== 0x5E/* ^ */) { return false; }
            if (silent) { return false; } // don't run any pairs in validation mode
            if (start + 2 >= max) { return false; }
            state.pos = start + 1;
            while (state.pos < max) {
                if (state.src.charCodeAt(state.pos) === 0x5E/* ^ */) {
                    found = true;
                    break;
                }
                state.md.inline.skipToken(state);
            }
            if (!found || start + 1 === state.pos) {
                state.pos = start;
                return false;
            }
            content = state.src.slice(start + 1, state.pos);
            // don't allow unescaped spaces/newlines inside
            if (content.match(/(^|[^\\])(\\\\)*\s/)) {
                state.pos = start;
                return false;
            }
            // found!
            state.posMax = state.pos;
            state.pos = start + 1;
            // Earlier we checked !silent, but this implementation does not need it
            token = state.push('sup_open', 'sup', 1);
            token.markup = '^';
            token = state.push('text', '', 0);
            token.content = content.replace(UNESCAPE_RE, '$1');
            token = state.push('sup_close', 'sup', -1);
            token.markup = '^';
            state.pos = state.posMax + 1;
            state.posMax = max;
            return true;
        }
        const subscript = (state, silent) => {
            let found,
                content,
                token,
                max = state.posMax,
                start = state.pos;
            if (state.src.charCodeAt(start) !== 0x7E/* ~ */) { return false; }
            if (silent) { return false; } // don't run any pairs in validation mode
            if (start + 2 >= max) { return false; }
            state.pos = start + 1;
            while (state.pos < max) {
                if (state.src.charCodeAt(state.pos) === 0x7E/* ~ */) {
                    found = true;
                    break;
                }
                state.md.inline.skipToken(state);
            }
            if (!found || start + 1 === state.pos) {
                state.pos = start;
                return false;
            }
            content = state.src.slice(start + 1, state.pos);
            // don't allow unescaped spaces/newlines inside
            if (content.match(/(^|[^\\])(\\\\)*\s/)) {
                state.pos = start;
                return false;
            }
            // found!
            state.posMax = state.pos;
            state.pos = start + 1;
            // Earlier we checked !silent, but this implementation does not need it
            token = state.push('sub_open', 'sub', 1);
            token.markup = '~';
            token = state.push('text', '', 0);
            token.content = content.replace(UNESCAPE_RE, '$1');
            token = state.push('sub_close', 'sub', -1);
            token.markup = '~';
            state.pos = state.posMax + 1;
            state.posMax = max;
            return true;
        }
        const mermaidMap = {};
        const mermaidQuene = new Map();
        let mermaidTimeout;
        let loadingMermaid = false;
        const loadMermaidQuene = () => {
            mermaidQuene.forEach((content, id) => { loadSingleMermaid(id, content) });
            mermaidQuene.clear();
        };
        const loadSingleMermaid = async (id, content) => {
            let contianer = document.querySelector("#" + id);
            if (!contianer || contianer.children.length) return;
            let result;
            try {
                let { svg } = await mermaid.render(mermaid.detectType(content), content, contianer);
                result = svg;
            } catch (error) {
                return;
            }
            result = result.replace(/(<svg[^>]*?)\sstyle="[^"]*"/i, '$1');
            mermaidMap[id] = result;
            document.querySelectorAll('#' + id).forEach(item => { item.innerHTML = result });
        };
        const loadRunMermaid = (id, content, bounce = false) => {
            if (typeof mermaid === 'undefined') {
                if (!loadingMermaid) {
                    loadingMermaid = true;
                    let script = document.createElement("script");
                    script.crossOrigin = "anonymous";
                    script.src = "https://fastly.jsdelivr.net/npm/mermaid@11.4.1/dist/mermaid.min.js";
                    script.onload = async () => {
                        loadingMermaid = false;
                        mermaid.mermaidAPI.initialize({
                            startOnLoad: false
                        });
                        loadMermaidQuene();
                    }
                    document.body.appendChild(script);
                }
                if (loading && !bounce) {
                    clearTimeout(mermaidTimeout);
                    mermaidTimeout = setTimeout(() => {
                        mermaidQuene.set(id, content);
                        if (typeof mermaid !== 'undefined') loadMermaidQuene();
                    }, 600)
                } else {
                    mermaidQuene.set(id, content);
                }
            } else {
                if (loading && !bounce) {
                    clearTimeout(mermaidTimeout);
                    mermaidTimeout = setTimeout(() => { loadSingleMermaid(id, content) }, 600)
                } else {
                    setTimeout(() => { loadSingleMermaid(id, content) });
                }
            }
        };
        const md = markdownit({
            breaks: true,
            linkify: true,
            highlight: function (str, lang) {
                try {
                    return hljs.highlightAuto(str).value;
                } catch (e) { }
                return "";
            }
        });
        md.inline.ruler.after("emphasis", "sup", superscript);
        md.inline.ruler.after("emphasis", "sub", subscript);
        md.use(texmath, { engine: katex, delimiters: ["brackets", "dollars"] });
        md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
            let aIndex = tokens[idx].attrIndex("target");
            if (tokens[idx + 1] && tokens[idx + 1].type === "image") tokens[idx].attrPush(["download", ""]);
            else if (aIndex < 0) tokens[idx].attrPush(["target", "_blank"]);
            else tokens[idx].attrs[aIndex][1] = "_blank";
            return self.renderToken(tokens, idx, options);
        };
        const codeUtils = {
            getCodeLang(str = "") {
                const res = str.match(/ class="language-(.*?)"/);
                return (res && res[1]) || "";
            },
            getFragment(str = "") {
                return str ? `<span class="u-mdic-copy-code_lang" text="${str}"></span>` : "";
            },
        };
        const getCodeLangFragment = (oriStr = "") => {
            return codeUtils.getFragment(codeUtils.getCodeLang(oriStr));
        };
        const copyClickCode = (ele) => {
            const input = document.createElement("textarea");
            input.value = ele.parentElement.nextElementSibling.textContent;
            const nDom = ele.previousElementSibling;
            const nDelay = ele.dataset.mdicNotifyDelay;
            const cDom = nDom.previousElementSibling;
            document.body.appendChild(input);
            input.select();
            input.setSelectionRange(0, input.value.length);
            document.execCommand("copy");
            document.body.removeChild(input);
            if (nDom.style.display === "none") {
                nDom.style.display = "block";
                cDom && (cDom.style.display = "none");
                setTimeout(() => {
                    nDom.style.display = "none";
                    cDom && (cDom.style.display = "block");
                }, nDelay);
            }
        };
        const copyClickMd = (idx) => {
            const input = document.createElement("textarea");
            input.value = data[idx].content;
            document.body.appendChild(input);
            input.select();
            input.setSelectionRange(0, input.value.length);
            document.execCommand("copy");
            document.body.removeChild(input);
        }
        const downloadSVG = (target, ev) => {
            if (ev.target !== target) {
                let blob = new Blob([target.innerHTML], { type: "image/svg+xml" });
                downBlob(blob, "mermaid-" + target.children[0].getAttribute("aria-roledescription") + ".svg")
            }
        }
        const enhanceCode = (render, options = {}) => (...args) => {
            const {
                btnText = translations[locale]["copyCode"], // button text
                successText = translations[locale]["copySuccess"], // copy-success text
                successTextDelay = 2000, // successText show time [ms]
                showCodeLanguage = true, // false | show code language
            } = options;
            const [tokens = [], idx = 0] = args;
            const originResult = render.apply(this, args);
            const langFrag = showCodeLanguage ? getCodeLangFragment(originResult) : "";
            const tpls = [
                '<div class="m-mdic-copy-wrapper">',
                `${langFrag}`,
                `<div class="u-mdic-copy-notify" style="display:none;" text="${successText}"></div>`,
                `<button class="u-mdic-copy-btn j-mdic-copy-btn" text="${btnText}" data-mdic-notify-delay="${successTextDelay}" onclick="copyClickCode(this)"></button>`,
                '</div>',
            ];
            if (tokens[idx].type === "fence" && langFrag.indexOf(`text="mermaid"`) !== -1) {
                let hash = "mermaid" + createSHA1().update(tokens[idx].content).digest("hex");
                if (mermaidMap[hash]) return originResult.replace("<pre>", `<pre>${tpls.join("")}`).replace("<pre>", `<div class="mermaid" id="${hash}" onclick="downloadSVG(this, event)">${mermaidMap[hash]}</div><pre>`);
                loadRunMermaid(hash, tokens[idx].content, tokens.length - 1 > idx);
                return originResult.replace("<pre>", `<pre>${tpls.join("")}`).replace("<pre>", `<div class="mermaid" id="${hash}" onclick="downloadSVG(this, event)"></div><pre>`)
            } else {
                return originResult.replace("<pre>", `<pre>${tpls.join("")}`);
            }
        };
        md.renderer.rules.code_block = enhanceCode(md.renderer.rules.code_block);
        md.renderer.rules.fence = enhanceCode(md.renderer.rules.fence);
        md.renderer.rules.image = function (tokens, idx, options, env, self) {
            let token = tokens[idx];
            token.attrs[token.attrIndex("alt")][1] = self.renderInlineAsText(token.children, options, env);
            token.attrSet("onload", "scrollToBottomLoad(this);this.removeAttribute('onload');this.removeAttribute('onerror')");
            token.attrSet("onerror", "scrollToBottomLoad(this);this.removeAttribute('onload');this.removeAttribute('onerror')");
            token.attrPush(["decoding", "async"]);
            token.attrPush(["loading", "lazy"]);
            return self.renderToken(tokens, idx, options)
        }
        let currentVoiceIdx;
        let editingIdx;
        let originText;
        const resumeSend = () => {
            if (editingIdx !== void 0) {
                chatlog.children[systemRole ? editingIdx - 1 : editingIdx].classList.remove("showEditReq");
            }
            sendBtnEle.children[0].textContent = translations[locale]["send"];
            inputAreaEle.value = originText;
            clearEle.title = translations[locale]["clearChat"];
            clearEle.classList.remove("closeConv");
            originText = void 0;
            editingIdx = void 0;
        }
        const mdOptionEvent = function (ev) {
            let id = ev.target.dataset.id;
            if (id) {
                let parent = ev.target.parentElement;
                let idxEle = parent.parentElement;
                let idx = Array.prototype.indexOf.call(chatlog.children, this.parentElement);
                if (id === "voiceBtn" || id === "speechMd" || id === "pauseMd" || id === "resumeMd") {
                    let classList = parent.dataset.id === "voiceBtn" ? parent.classList : ev.target.classList;
                    if (classList.contains("readyVoice")) {
                        if (chatlog.children[idx].dataset.loading !== "true") {
                            idx = systemRole ? idx + 1 : idx;
                            speechEvent(idx);
                        }
                    } else if (classList.contains("pauseVoice")) {
                        if (voiceIns) {
                            if (voiceIns instanceof Audio) voiceIns.pause();
                            else {
                                if (supportSpe) speechSynthesis.pause();
                                classList.remove("readyVoice");
                                classList.remove("pauseVoice");
                                classList.add("resumeVoice");
                            }
                        }
                    } else {
                        if (voiceIns) {
                            if (voiceIns instanceof Audio) voiceIns.play();
                            else {
                                if (supportSpe) speechSynthesis.resume();
                                classList.remove("readyVoice");
                                classList.remove("resumeVoice");
                                classList.add("pauseVoice");
                            }
                        }
                    }
                } else if (id === "editMd") {
                    let reqEle = chatlog.children[idx];
                    idx = systemRole ? idx + 1 : idx;
                    if (editingIdx === idx) return;
                    if (editingIdx !== void 0) {
                        chatListEle.children[systemRole ? editingIdx - 1 : editingIdx].classList.remove("showEditReq");
                    }
                    reqEle.classList.add("showEditReq");
                    editingIdx = idx;
                    originText = inputAreaEle.value;
                    inputAreaEle.value = data[idx].content;
                    inputAreaEle.dispatchEvent(new Event("input"));
                    inputAreaEle.focus();
                    sendBtnEle.children[0].textContent = translations[locale]["update"];
                    clearEle.title = translations[locale]["cancel"];
                    clearEle.classList.add("closeConv");
                } else if (id === "refreshMd") {
                    if (noLoading()) {
                        formatAvatarEle(chatlog.children[idx].children[0], modelVersion);
                        if (ev.target.classList.contains("refreshReq")) {
                            chatlog.children[idx].children[1].innerHTML = "<p class='cursorCls'><br /></p>";
                            chatlog.children[idx].dataset.loading = true;
                            idx = systemRole ? idx + 1 : idx;
                            data[idx].content = "";
                            if (idx === data.findIndex(item => { return item.role === "assistant" })) activeChatEle.children[1].children[1].textContent = "";
                            if (idx === currentVoiceIdx) endSpeak();
                            loadAction(true);
                            refreshIdx = idx;
                            streamGen();
                        } else {
                            chatlog.children[idx].dataset.loading = true;
                            idx = systemRole ? idx + 1 : idx;
                            progressData = data[idx].content;
                            loadAction(true);
                            refreshIdx = idx;
                            streamGen(true);
                        }
                    }
                } else if (id === "copyMd") {
                    idx = systemRole ? idx + 1 : idx;
                    copyClickMd(idx);
                    notyf.success(translations[locale]["copySuccess"]);
                } else if (id === "delMd") {
                    if (noLoading()) {
                        if (confirmAction(translations[locale]["delMsgTip"])) {
                            chatlog.removeChild(chatlog.children[idx]);
                            idx = systemRole ? idx + 1 : idx;
                            let firstIdx = data.findIndex(item => { return item.role === "assistant" });
                            if (currentVoiceIdx !== void 0) {
                                if (currentVoiceIdx === idx) { endSpeak() }
                                else if (currentVoiceIdx > idx) { currentVoiceIdx-- }
                            }
                            if (editingIdx !== void 0) {
                                if (editingIdx === idx) { resumeSend() }
                                else if (editingIdx > idx) { editingIdx-- }
                            }
                            data.splice(idx, 1);
                            if (firstIdx === idx) updateChatPre();
                            updateChats();
                        }
                    }
                } else if (id === "downAudioMd") {
                    if (chatlog.children[idx].dataset.loading !== "true") {
                        idx = systemRole ? idx + 1 : idx;
                        downloadAudio(idx);
                    }
                }
            }
        }
        const formatAvatarEle = (ele, model) => {
            ele.className = "chatAvatar";
            if (ele.parentElement.className === "request") {
                ele.innerHTML = `<img src="${userAvatar}" />`;
            } else {
                if (model.startsWith("gpt") || model.startsWith("o")) {
                    ele.classList.add("gptAvatar")
                    ele.innerHTML = `<svg width="24" height="24"><use xlink:href="#aiIcon"></use></svg>`;
                } else if (model.startsWith("deepseek")) {
                    ele.innerHTML = `<svg width="30" height="30"><use xlink:href="#deepseekIcon"></use></svg>`;
                } else if (model.startsWith("azure")) {
                    ele.innerHTML = `<svg width="30" height="30"><use xlink:href="#azureAIIcon"></use></svg>`;
                } else if (model.startsWith("gemini")) {
                    ele.innerHTML = `<svg width="30" height="30"><use xlink:href="#geminiIcon"></use></svg>`;
                } else {
                    ele.innerHTML = `<svg width="30" height="30"><use xlink:href="#claudeIcon"></use></svg>`;
                }
            }
        }
        const formatMdEle = (ele, model) => {
            let avatar = document.createElement("div");
            ele.appendChild(avatar);
            formatAvatarEle(avatar, model);
            let realMd = document.createElement("div");
            realMd.className = ele.className === "request" ? "requestBody" : "responseBody markdown-body";
            ele.appendChild(realMd);
            let mdOption = document.createElement("div");
            mdOption.className = "mdOption";
            ele.appendChild(mdOption);
            let optionWidth = existVoice >= 2 ? 140 : 105;
            mdOption.innerHTML += `<div class="optionItems" style="width:${optionWidth}px;left:-${optionWidth - 10}px">`
                + (ele.className === "request" ? `<div data-id="editMd" class="optionItem" title="${translations[locale]["edit"]}">
                <svg width="18" height="18"><use xlink:href="#chatEditIcon" /></svg>
                </div>` : `<div data-id="refreshMd" class="refreshReq optionItem" title="${translations[locale]["refresh"]}">
                <svg width="18" height="18" ><use xlink:href="#refreshIcon" /></svg>
                <svg width="18" height="18" ><use xlink:href="#halfRefIcon" /></svg>
                </div>`) +
                `<div data-id="copyMd" class="optionItem" title="${translations[locale]["copy"]}">
                <svg width="20" height="20"><use xlink:href="#copyIcon" /></svg>
            </div>
            <div data-id="delMd" class="optionItem" title="${translations[locale]["del"]}">
                <svg width="20" height="20"><use xlink:href="#delIcon" /></svg>
            </div>` + (existVoice >= 2 ? `<div data-id="downAudioMd" class="optionItem" title="${translations[locale]["downAudio"]}">
                <svg width="20" height="20"><use xlink:href="#downAudioIcon" /></svg>
            </div>` : "") + `</div>`;
            if (existVoice) {
                mdOption.innerHTML += `<div class="voiceCls readyVoice" data-id="voiceBtn">
                <svg width="20" height="20" role="img" data-id="speechMd"><title>${translations[locale]["speech"]}</title><use xlink:href="#readyVoiceIcon" /></svg>
                <svg width="20" height="20" role="img" data-id="pauseMd"><title>${translations[locale]["pause"]}</title><use xlink:href="#pauseVoiceIcon" /></svg>
                <svg width="20" height="20" role="img" data-id="resumeMd"><title>${translations[locale]["resume"]}</title><use xlink:href="#resumeVoiceIcon" /></svg>
                </div>`
            }
            mdOption.onclick = mdOptionEvent;
        }
        let allListEle = chatListEle.parentElement;
        let folderData = [];
        let chatsData = [];
        let chatIdxs = [];
        let searchIdxs = [];
        let activeChatIdx = 0;
        let activeChatEle;
        let operateChatIdx, operateFolderIdx;
        let dragLi, dragType, dragIdx;
        let mobileDragOut;
        const mobileDragStartEV = function (ev) {
            if (mobileDragOut !== void 0) {
                clearTimeout(mobileDragOut);
                mobileDragOut = void 0;
            }
            mobileDragOut = setTimeout(() => {
                this.setAttribute("draggable", "true");
                this.dispatchEvent(ev);
            }, 200);
        };
        if (isMobile) {
            let stopDragOut = () => {
                if (mobileDragOut !== void 0) {
                    clearTimeout(mobileDragOut);
                    mobileDragOut = void 0;
                }
            };
            let stopDrag = () => {
                stopDragOut();
                document.querySelectorAll("[draggable=true]").forEach(ele => {
                    ele.setAttribute("draggable", "false");
                })
            };
            document.body.addEventListener("touchmove", stopDragOut);
            document.body.addEventListener("touchend", stopDrag);
            document.body.addEventListener("touchcancel", stopDrag);
        };
        const delDragIdx = () => {
            let chatIdx = chatIdxs.indexOf(dragIdx);
            if (chatIdx !== -1) {
                chatIdxs.splice(chatIdx, 1);
            } else {
                folderData.forEach((item, i) => {
                    let inIdx = item.idxs.indexOf(dragIdx);
                    if (inIdx !== -1) {
                        item.idxs.splice(inIdx, 1);
                        updateFolder(i);
                    }
                })
            }
        }
        const updateFolder = (idx) => {
            let folderEle = folderListEle.children[idx];
            let childLen = folderData[idx].idxs.length;
            folderEle.children[0].children[1].children[1].textContent = childLen + translations[locale]["chats"];
            folderEle.classList.toggle("expandFolder", childLen);
        }
        folderListEle.ondragenter = chatListEle.ondragenter = function (ev) {
            ev.preventDefault();
            if (ev.target === dragLi) return;
            allListEle.querySelectorAll(".dragingChat").forEach(ele => {
                ele.classList.remove("dragingChat");
            })
            if (dragType === "chat") {
                if (this === chatListEle) {
                    this.classList.add("dragingChat");
                    let dragindex = Array.prototype.indexOf.call(chatListEle.children, dragLi);
                    let targetindex = Array.prototype.indexOf.call(chatListEle.children, ev.target);
                    delDragIdx();
                    if (targetindex !== -1) {
                        chatIdxs.splice(targetindex, 0, dragIdx);
                        if (dragindex === -1 || dragindex >= targetindex) {
                            chatListEle.insertBefore(dragLi, ev.target);
                        } else {
                            chatListEle.insertBefore(dragLi, ev.target.nextElementSibling);
                        }
                    } else {
                        chatIdxs.push(dragIdx);
                        chatListEle.appendChild(dragLi);
                    }
                } else if (this === folderListEle) {
                    let folderIdx;
                    if (ev.target.classList.contains("headLi")) {
                        ev.target.parentElement.classList.add("dragingChat");
                        ev.target.nextElementSibling.appendChild(dragLi);
                        delDragIdx();
                        folderIdx = Array.prototype.indexOf.call(folderListEle.children, ev.target.parentElement);
                        folderData[folderIdx].idxs.push(dragIdx);
                        updateFolder(folderIdx);
                    } else if (ev.target.classList.contains("chatLi")) {
                        ev.target.parentElement.parentElement.classList.add("dragingChat");
                        let parent = ev.target.parentElement;
                        delDragIdx();
                        folderIdx = Array.prototype.indexOf.call(folderListEle.children, parent.parentElement);
                        let dragindex = Array.prototype.indexOf.call(parent.children, dragLi);
                        let targetindex = Array.prototype.indexOf.call(parent.children, ev.target);
                        if (dragindex !== -1) {
                            folderData[folderIdx].idxs.splice(targetindex, 0, dragIdx);
                            if (dragindex < targetindex) {
                                parent.insertBefore(dragLi, ev.target.nextElementSibling);
                            } else {
                                parent.insertBefore(dragLi, ev.target);
                            }
                        } else {
                            folderData[folderIdx].idxs.push(dragIdx);
                            parent.appendChild(dragLi);
                        }
                        updateFolder(folderIdx);
                    }
                }
                updateChatIdxs();
            } else if (dragType === "folder") {
                if (this === folderListEle) {
                    let dragindex = Array.prototype.indexOf.call(folderListEle.children, dragLi);
                    let folderIdx = Array.prototype.findIndex.call(folderListEle.children, (item) => {
                        return item.contains(ev.target);
                    })
                    folderListEle.children[folderIdx].classList.remove("expandFolder");
                    let folderEle = folderListEle.children[folderIdx];
                    let data = folderData.splice(dragindex, 1)[0];
                    folderData.splice(folderIdx, 0, data);
                    if (dragindex === -1 || dragindex >= folderIdx) {
                        folderListEle.insertBefore(dragLi, folderEle);
                    } else {
                        folderListEle.insertBefore(dragLi, folderEle.nextElementSibling);
                    }
                    updateChatIdxs();
                }
            }
        }
        folderListEle.ondragover = chatListEle.ondragover = (ev) => {
            ev.preventDefault();
        }
        folderListEle.ondragend = chatListEle.ondragend = (ev) => {
            document.getElementsByClassName("dragingLi")[0].classList.remove("dragingLi");
            allListEle.querySelectorAll(".dragingChat").forEach(ele => {
                ele.classList.remove("dragingChat");
            })
            dragType = dragIdx = dragLi = void 0;
        }
        const chatDragStartEv = function (ev) {
            ev.stopPropagation();
            dragLi = this;
            dragLi.classList.add("dragingLi");
            dragType = "chat";
            if (chatListEle.contains(this)) {
                let idx = Array.prototype.indexOf.call(chatListEle.children, this);
                dragIdx = chatIdxs[idx];
            } else if (folderListEle.contains(this)) {
                let folderIdx = Array.prototype.indexOf.call(folderListEle.children, this.parentElement.parentElement);
                let inFolderIdx = Array.prototype.indexOf.call(this.parentElement.children, this);
                dragIdx = folderData[folderIdx].idxs[inFolderIdx];
            }
        }
        const extraFolderActive = (folderIdx) => {
            let folderNewIdx = -1;
            for (let i = folderIdx - 1; i >= 0; i--) {
                if (folderData[i].idxs.length) {
                    folderNewIdx = i;
                }
            }
            if (folderNewIdx === -1) {
                for (let i = folderIdx + 1; i < folderData.length; i++) {
                    if (folderData[i].idxs.length) folderNewIdx = i;
                }
            }
            if (folderNewIdx !== -1) {
                activeChatIdx = folderData[folderNewIdx].idxs[0];
            } else if (chatIdxs.length) {
                activeChatIdx = chatIdxs[0];
            } else {
                activeChatIdx = -1;
            }
        }
        const delFolder = (folderIdx, ele) => {
            if (confirmAction(translations[locale]["delFolderTip"])) {
                let delData = folderData[folderIdx];
                let idxs = delData.idxs.sort();
                ele.parentElement.remove();
                if (idxs.indexOf(activeChatIdx) !== -1) {
                    endAll();
                    extraFolderActive(folderIdx);
                }
                folderData.splice(folderIdx, 1);
                for (let i = idxs.length - 1; i >= 0; i--) {
                    chatsData.splice(idxs[i], 1);
                }
                folderData.forEach(item => {
                    if (item.idxs.length) {
                        item.idxs.forEach((i, ix) => {
                            let len = idxs.filter(j => { return i > j }).length;
                            if (len) {
                                item.idxs[ix] = i - len;
                            }
                        })
                    }
                })
                chatIdxs.forEach((item, ix) => {
                    let len = idxs.filter(j => { return item > j }).length;
                    if (len) chatIdxs[ix] = item - len;
                })
                let len = idxs.filter(j => { return activeChatIdx > j }).length;
                if (len) activeChatIdx -= len;
                if (activeChatIdx === -1) {
                    addNewChat();
                    activeChatIdx = 0;
                    chatEleAdd(activeChatIdx);
                }
                updateChats();
                activeChat();
            }
        }
        const folderAddChat = (folderIdx, headEle) => {
            endAll();
            let chat = { name: translations[locale]["newChatName"], data: [] };
            chatsData.push(chat);
            activeChatIdx = chatsData.length - 1;
            folderData[folderIdx].idxs.push(activeChatIdx);
            let ele = chatEleAdd(activeChatIdx, false)
            headEle.nextElementSibling.appendChild(ele);
            updateFolder(folderIdx);
            updateChats();
            activeChat(ele);
        }
        const folderEleEvent = function (ev) {
            ev.preventDefault();
            ev.stopPropagation();
            let parent = this.parentElement;
            let idx = Array.prototype.indexOf.call(folderListEle.children, parent);
            if (ev.target.className === "headLi") {
                let isExpanded = parent.classList.toggle("expandFolder");
                if (folderData[idx].idxs.indexOf(activeChatIdx) !== -1) {
                    parent.classList.toggle("activeFolder", !isExpanded);
                }
            } else if (ev.target.dataset.type === "folderAddChat") {
                folderAddChat(idx, this);
            } else if (ev.target.dataset.type === "folderEdit") {
                toEditName(idx, this, 0);
            } else if (ev.target.dataset.type === "folderDel") {
                delFolder(idx, this);
            }
        }
        const folderDragStartEv = function (ev) {
            dragLi = this;
            dragLi.classList.add("dragingLi");
            dragType = "folder";
            dragIdx = Array.prototype.indexOf.call(folderListEle.children, this);
        }
        const folderEleAdd = (idx, push = true) => {
            let folder = folderData[idx];
            let folderEle = document.createElement("div");
            folderEle.className = "folderLi";
            if (!isMobile) folderEle.setAttribute("draggable", "true");
            else folderEle.ontouchstart = mobileDragStartEV;
            let headEle = document.createElement("div");
            headEle.className = "headLi";
            headEle.innerHTML = `<svg width="24" height="24"><use xlink:href="#expandFolderIcon" /></svg>
                <div class="folderInfo">
                    <div class="folderName"></div>
                    <div class="folderNum"></div>
                </div>
                <div class="folderOption">
                    <svg data-type="folderAddChat" width="24" height="24" role="img"><title>${translations[locale]["newChat"]}</title><use xlink:href="#addIcon" /></svg>
                    <svg data-type="folderEdit" width="24" height="24" role="img"><title>${translations[locale]["edit"]}</title><use xlink:href="#chatEditIcon" /></svg>
                    <svg data-type="folderDel" width="24" height="24" role="img"><title>${translations[locale]["del"]}</title><use xlink:href="#delIcon" /></svg>
                </div>`
            headEle.children[1].children[0].textContent = folder.name;
            headEle.children[1].children[1].textContent = folder.idxs.length + translations[locale]["chats"];
            folderEle.appendChild(headEle);
            folderEle.ondragstart = folderDragStartEv;
            headEle.onclick = folderEleEvent;
            let chatsEle = document.createElement("div");
            chatsEle.className = "chatsInFolder";
            for (let i = 0; i < folder.idxs.length; i++) {
                chatsEle.appendChild(chatEleAdd(folder.idxs[i], false));
            }
            folderEle.appendChild(chatsEle);
            if (push) { folderListEle.appendChild(folderEle) }
            else { folderListEle.insertBefore(folderEle, folderListEle.firstChild) }
        }
        document.getElementById("newFolder").onclick = function () {
            folderData.unshift({ name: translations[locale]["newFolderName"], idxs: [] });
            folderEleAdd(0, false);
            updateChatIdxs();
            folderListEle.parentElement.scrollTop = 0;
        };
        const initChatEle = (index, chatEle) => {
            chatEle.children[1].children[0].textContent = chatsData[index].name;
            let chatPreview = "";
            if (chatsData[index].data && chatsData[index].data.length) {
                let first = chatsData[index].data.find(item => { return item.role === "assistant" });
                if (first) { chatPreview = first.content.slice(0, 30) }
            }
            chatEle.children[1].children[1].textContent = chatPreview;
        };
        const chatEleAdd = (idx, appendChat = true) => {
            let chat = chatsData[idx];
            let chatEle = document.createElement("div");
            chatEle.className = "chatLi";
            if (!isMobile) chatEle.setAttribute("draggable", "true");
            else chatEle.ontouchstart = mobileDragStartEV;
            chatEle.ondragstart = chatDragStartEv;
            chatEle.innerHTML = `<svg width="24" height="24"><use xlink:href="#chatIcon" /></svg>
                <div class="chatInfo">
                    <div class="chatName"></div>
                    <div class="chatPre"></div>
                </div>
                <div class="chatOption"><svg data-type="chatEdit" width="24" height="24" role="img"><title>${translations[locale]["edit"]}</title><use xlink:href="#chatEditIcon" /></svg>
                <svg data-type="chatDel" width="24" height="24" role="img"><title>${translations[locale]["del"]}</title><use xlink:href="#delIcon" /></svg></div>`
            if (appendChat) chatListEle.appendChild(chatEle);
            initChatEle(idx, chatEle);
            chatEle.onclick = chatEleEvent;
            return chatEle;
        };
        const addNewChat = () => {
            let chat = { name: translations[locale]["newChatName"], data: [] };
            if (presetRoleData.default) chat.data.unshift({ role: "system", content: presetRoleData.default });
            preEle.selectedIndex = 0;
            chatsData.push(chat);
            chatIdxs.push(chatsData.length - 1);
            updateChats();
        };
        const delChat = (idx, ele, folderIdx, inFolderIdx) => {
            if (confirmAction(translations[locale]["delChatTip"])) {
                if (idx === activeChatIdx) endAll();
                if (folderIdx !== void 0) {
                    let folder = folderData[folderIdx];
                    folder.idxs.splice(inFolderIdx, 1);
                    updateFolder(folderIdx);
                    if (idx === activeChatIdx) {
                        if (inFolderIdx - 1 >= 0) {
                            activeChatIdx = folder.idxs[inFolderIdx - 1];
                        } else if (folder.idxs.length) {
                            activeChatIdx = folder.idxs[0];
                        } else {
                            extraFolderActive(folderIdx);
                        }
                    }
                } else {
                    let chatIdx = chatIdxs.indexOf(idx);
                    chatIdxs.splice(chatIdx, 1);
                    if (idx === activeChatIdx) {
                        if (chatIdx - 1 >= 0) {
                            activeChatIdx = chatIdxs[chatIdx - 1];
                        } else if (chatIdxs.length) {
                            activeChatIdx = chatIdxs[0];
                        } else {
                            let folderNewIdx = -1;
                            for (let i = folderData.length - 1; i >= 0; i--) {
                                if (folderData[i].idxs.length) folderNewIdx = i;
                            }
                            if (folderNewIdx !== -1) {
                                activeChatIdx = folderData[folderNewIdx].idxs[0];
                            } else {
                                activeChatIdx = -1;
                            }
                        }
                    }
                }
                if (activeChatIdx > idx) activeChatIdx--;
                chatsData.splice(idx, 1);
                ele.remove();
                folderData.forEach(item => {
                    if (item.idxs.length) {
                        item.idxs.forEach((i, ix) => {
                            if (i > idx) item.idxs[ix] = i - 1;
                        })
                    }
                })
                chatIdxs.forEach((item, ix) => {
                    if (item > idx) chatIdxs[ix] = item - 1;
                })
                if (activeChatIdx === -1) {
                    addNewChat();
                    activeChatIdx = 0;
                    chatEleAdd(activeChatIdx);
                }
                updateChats();
                activeChat();
            }
        };
        const endEditEvent = (ev) => {
            if (!document.getElementById("activeChatEdit").contains(ev.target)) {
                endEditChat();
            }
        };
        const preventDrag = (ev) => {
            ev.preventDefault();
            ev.stopPropagation();
        }
        const endEditChat = () => {
            if (operateChatIdx !== void 0) {
                let ele = getChatEle(operateChatIdx);
                chatsData[operateChatIdx].name = ele.children[1].children[0].textContent = document.getElementById("activeChatEdit").value;
                ele.lastElementChild.remove();
            } else if (operateFolderIdx !== void 0) {
                let ele = folderListEle.children[operateFolderIdx].children[0];
                folderData[operateFolderIdx].name = ele.children[1].children[0].textContent = document.getElementById("activeChatEdit").value;
                ele.lastElementChild.remove();
            }
            updateChats();
            operateChatIdx = operateFolderIdx = void 0;
            document.body.removeEventListener("mousedown", endEditEvent, true);
        }
        const toEditName = (idx, ele, type) => {
            let inputEle = document.createElement("input");
            inputEle.id = "activeChatEdit";
            inputEle.setAttribute("draggable", "true");
            inputEle.ondragstart = preventDrag;
            ele.appendChild(inputEle);
            if (type) {
                inputEle.value = chatsData[idx].name;
                operateChatIdx = idx;
            } else {
                inputEle.value = folderData[idx].name;
                operateFolderIdx = idx;
            }
            inputEle.setSelectionRange(0, 0);
            inputEle.focus();
            inputEle.onkeydown = (e) => {
                if (e.keyCode === 13) {
                    e.preventDefault();
                    endEditChat();
                }
            };
            document.body.addEventListener("mousedown", endEditEvent, true);
            return inputEle;
        };
        const chatEleEvent = function (ev) {
            ev.preventDefault();
            ev.stopPropagation();
            let idx, folderIdx, inFolderIdx;
            if (chatListEle.contains(this)) {
                idx = Array.prototype.indexOf.call(chatListEle.children, this);
                idx = chatIdxs[idx];
            } else if (folderListEle.contains(this)) {
                folderIdx = Array.prototype.indexOf.call(folderListEle.children, this.parentElement.parentElement);
                inFolderIdx = Array.prototype.indexOf.call(this.parentElement.children, this);
                idx = folderData[folderIdx].idxs[inFolderIdx];
            }
            if (ev.target.classList.contains("chatLi")) {
                if (searchChatEle.value || activeChatIdx !== idx) {
                    endAll();
                    activeChatIdx = idx;
                    activeChat(this);
                }
                if (window.innerWidth <= 800) {
                    document.body.classList.remove("show-nav");
                }
            } else if (ev.target.dataset.type === "chatEdit") {
                toEditName(idx, this, 1);
            } else if (ev.target.dataset.type === "chatDel") {
                delChat(idx, this, folderIdx, inFolderIdx);
            }
        };
        const updateChats = () => {
            localStorage.setItem("chats", JSON.stringify(chatsData));
            updateChatIdxs();
        };
        const updateChatIdxs = () => {
            localStorage.setItem("chatIdxs", JSON.stringify(chatIdxs));
            localStorage.setItem("folders", JSON.stringify(folderData));
        }
        const createConvEle = (className, append = true, model) => {
            let div = document.createElement("div");
            div.className = className;
            formatMdEle(div, model);
            if (append) chatlog.appendChild(div);
            return div;
        }
        const getChatEle = (idx) => {
            let chatIdx = chatIdxs.indexOf(idx);
            if (chatIdx !== -1) {
                return chatListEle.children[chatIdx];
            } else {
                let inFolderIdx;
                let folderIdx = folderData.findIndex(item => {
                    inFolderIdx = item.idxs.indexOf(idx);
                    return inFolderIdx !== -1;
                })
                if (folderIdx !== -1) {
                    return folderListEle.children[folderIdx].children[1].children[inFolderIdx];
                }
            }
        }
        const renderData = (dat) => {
            let contentHTML = md.render(dat.content) || "<br />";
            if (dat.reasoning_content) return `<div><div class="cotBtn endCot" data-i18n-key="thinked" onclick="toggleCot(this, event)">${translations[locale]["thinked"]}</div><div class="cotContent">${md.render(dat.reasoning_content)}</div></div><div class="markdown-body">${contentHTML}</div>`;
            else return contentHTML;
        }
        const activeChat = (ele) => {
            data = chatsData[activeChatIdx]["data"];
            allListEle.querySelectorAll(".activeChatLi").forEach(ele => {
                ele.classList.remove("activeChatLi");
            })
            allListEle.querySelectorAll(".activeFolder").forEach(ele => {
                ele.classList.remove("activeFolder")
            })
            if (!ele) ele = getChatEle(activeChatIdx);
            ele.classList.add("activeChatLi");
            activeChatEle = ele;
            if (chatIdxs.indexOf(activeChatIdx) === -1) {
                if (!ele.parentElement.parentElement.classList.contains("expandFolder")) {
                    ele.parentElement.parentElement.classList.add("activeFolder");
                }
            }
            if (data[0] && data[0].role === "system") {
                systemRole = data[0].content;
                systemEle.value = systemRole;
            } else {
                systemRole = void 0;
                systemEle.value = "";
            }
            chatlog.innerHTML = "";
            if (systemRole ? data.length - 1 : data.length) {
                let firstIdx = systemRole ? 1 : 0;
                for (let i = firstIdx; i < data.length; i++) {
                    if (data[i].role === "user") {
                        createConvEle("request").children[1].textContent = data[i].content;
                    } else {
                        createConvEle("response", true, data[i].model).children[1].innerHTML = renderData(data[i]);
                    }
                }
            }
            let top = ele.offsetTop + ele.offsetHeight - allListEle.clientHeight;
            if (allListEle.scrollTop < top) allListEle.scrollTop = top;
            localStorage.setItem("activeChatIdx", activeChatIdx);
            if (searchIdxs[activeChatIdx] !== void 0) {
                let dataIdx = searchIdxs[activeChatIdx];
                if (dataIdx !== -1) {
                    let currChatEle = chatlog.children[systemRole ? dataIdx - 1 : dataIdx];
                    let childs = currChatEle.children[1].getElementsByTagName("*");
                    if (childs.length) {
                        for (let i = childs.length - 1; i >= 0; i--) {
                            if (childs[i].textContent && childs[i].textContent.indexOf(searchChatEle.value) !== -1) {
                                let offTop = findOffsetTop(childs[i], messagesEle);
                                messagesEle.scrollTop = offTop + childs[i].offsetHeight - messagesEle.clientHeight * 0.15;
                                break;
                            }
                        }
                    } else messagesEle.scrollTop = currChatEle.offsetTop;
                } else messagesEle.scrollTop = 0;
            }
        };
        newChatEle.onclick = () => {
            endAll();
            addNewChat();
            activeChatIdx = chatsData.length - 1;
            chatEleAdd(activeChatIdx);
            activeChat(chatListEle.lastElementChild);
        };
        const initChats = () => {
            let localChats = localStorage.getItem("chats");
            let localFolders = localStorage.getItem("folders");
            let localChatIdxs = localStorage.getItem("chatIdxs")
            let localChatIdx = localStorage.getItem("activeChatIdx");
            activeChatIdx = (localChatIdx && parseInt(localChatIdx)) || 0;
            if (localChats) {
                if (isCompressedChats) localChats = new TextDecoder().decode(inflateSync(stringToUint(localChats)));
                chatsData = JSON.parse(localChats);
                let folderIdxs = [];
                if (localFolders) {
                    folderData = JSON.parse(localFolders);
                    for (let i = 0; i < folderData.length; i++) {
                        folderEleAdd(i);
                        folderIdxs.push(...folderData[i].idxs);
                    }
                }
                if (localChatIdxs) {
                    chatIdxs = JSON.parse(localChatIdxs);
                    for (let i = 0; i < chatIdxs.length; i++) {
                        chatEleAdd(chatIdxs[i]);
                    }
                } else {
                    for (let i = 0; i < chatsData.length; i++) {
                        if (folderIdxs.indexOf(i) === -1) {
                            chatIdxs.push(i);
                            chatEleAdd(i);
                        }
                    }
                    updateChatIdxs();
                }
            } else {
                addNewChat();
                chatEleAdd(activeChatIdx);
            }
        };
        const initExpanded = () => {
            let folderIdx = folderData.findIndex(item => {
                return item.idxs.indexOf(activeChatIdx) !== -1;
            })
            if (folderIdx !== -1) {
                folderListEle.children[folderIdx].classList.add("expandFolder");
            }
        }
        initChats();
        initExpanded();
        activeChat();
        document.getElementById("clearSearch").onclick = () => {
            searchChatEle.value = "";
            searchChatEle.dispatchEvent(new Event("input"));
            searchChatEle.focus();
        }
        const toSearchChats = () => {
            searchIdxs.length = 0;
            for (let i = 0; i < chatsData.length; i++) {
                let chatEle = getChatEle(i);
                chatEle.style.display = null;
                let flags = isCaseSearch ? "" : "i";
                let pattern = escapeRegexExp(searchChatEle.value);
                let regex = new RegExp(pattern, flags);
                let nameData = chatsData[i].name.match(regex);
                let nameIdx = nameData ? nameData.index : -1;
                let matchContent;
                let dataIdx = chatsData[i].data.findIndex(item => {
                    return item.role !== "system" && (matchContent = item.content.match(regex))
                })
                if (nameIdx !== -1 || dataIdx !== -1) {
                    let ele = chatEle.children[1];
                    if (dataIdx !== -1) {
                        let data = chatsData[i].data[dataIdx];
                        let idx = matchContent.index;
                        let endIdx = idx + matchContent[0].length;
                        ele.children[1].textContent = (idx > 8 ? "..." : "") + data.content.slice(idx > 8 ? idx - 5 : 0, idx);
                        ele.children[1].appendChild(document.createElement("span"));
                        ele.children[1].lastChild.textContent = data.content.slice(idx, endIdx);
                        ele.children[1].appendChild(document.createTextNode(data.content.slice(endIdx)))
                    } else {
                        initChatEle(i, chatEle);
                    }
                    if (nameIdx !== -1) {
                        let endIdx = nameIdx + nameData[0].length;
                        ele.children[0].textContent = (nameIdx > 5 ? "..." : "") + chatsData[i].name.slice(nameIdx > 5 ? nameIdx - 3 : 0, nameIdx);
                        ele.children[0].appendChild(document.createElement("span"));
                        ele.children[0].lastChild.textContent = chatsData[i].name.slice(nameIdx, endIdx);
                        ele.children[0].appendChild(document.createTextNode(chatsData[i].name.slice(endIdx)))
                    } else {
                        ele.children[0].textContent = chatsData[i].name;
                    }
                    searchIdxs[i] = dataIdx;
                } else {
                    chatEle.style.display = "none";
                    initChatEle(i, chatEle);
                }
            }
            for (let i = 0; i < folderListEle.children.length; i++) {
                let folderChatEle = folderListEle.children[i].children[1];
                if (!folderChatEle.children.length || Array.prototype.filter.call(folderChatEle.children, (ele) => {
                    return ele.style.display !== "none"
                }).length === 0) {
                    folderListEle.children[i].style.display = "none";
                }
            }
        }
        searchChatEle.oninput = (ev) => {
            if (searchChatEle.value.length) {
                toSearchChats();
            } else {
                searchIdxs.length = 0;
                for (let i = 0; i < chatsData.length; i++) {
                    let chatEle = getChatEle(i);
                    chatEle.style.display = null;
                    initChatEle(i, chatEle);
                }
                for (let i = 0; i < folderListEle.children.length; i++) {
                    folderListEle.children[i].style.display = null;
                }
            }
        };
        document.getElementById("resetHotKey").onclick = () => {
            localStorage.removeItem("hotKeys");
            initHotKey();
            notyf.success(translations[locale]["resetSetSuccTip"]);
        };
        const blobToText = (blob) => {
            return new Promise((res, rej) => {
                let reader = new FileReader();
                reader.readAsText(blob);
                reader.onload = () => {
                    res(reader.result);
                }
                reader.onerror = (error) => {
                    rej(error);
                }
            })
        };
        document.getElementById("exportChat").onclick = () => {
            if (loading) stopLoading();
            let data = {
                chatsData: chatsData,
                folderData: folderData,
                chatIdxs: chatIdxs
            }
            let blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
            let date = new Date();
            let fileName = "chats-" + date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + ".json";
            downBlob(blob, fileName);
            notyf.success(translations[locale]["exportSuccTip"]);
        };
        document.getElementById("importChatInput").onchange = function () {
            let file = this.files[0];
            blobToText(file).then(text => {
                try {
                    let json = JSON.parse(text);
                    let checked = json.chatsData && json.folderData && json.chatIdxs && json.chatsData.every(item => {
                        return item.name !== void 0 && item.data !== void 0;
                    });
                    if (checked) {
                        let preFolder = folderData.length;
                        let preLen = chatsData.length;
                        if (json.chatsData) {
                            chatsData = chatsData.concat(json.chatsData);
                        }
                        if (json.folderData) {
                            for (let i = 0; i < json.folderData.length; i++) {
                                json.folderData[i].idxs = json.folderData[i].idxs.map(item => {
                                    return item + preLen;
                                })
                                folderData.push(json.folderData[i]);
                                folderEleAdd(i + preFolder);
                            }
                        }
                        if (json.chatIdxs) {
                            for (let i = 0; i < json.chatIdxs.length; i++) {
                                let newIdx = json.chatIdxs[i] + preLen;
                                chatIdxs.push(newIdx)
                                chatEleAdd(newIdx);
                            }
                        }
                        updateChats();
                        checkStorage();
                        notyf.success(translations[locale]["importSuccTip"]);
                    } else {
                        throw new Error("fmt error");
                    }
                } catch (e) {
                    notyf.error(translations[locale]["importFailTip"]);
                }
                this.value = "";
            })
        };
        clearChatSet.onclick = clearChat.onclick = () => {
            if (confirmAction(translations[locale]["clearAllTip"])) {
                chatsData.length = 0;
                chatIdxs.length = 0;
                folderData.length = 0;
                folderListEle.innerHTML = "";
                chatListEle.innerHTML = "";
                endAll();
                addNewChat();
                activeChatIdx = 0;
                chatEleAdd(activeChatIdx);
                localStorage.removeItem("compressedChats");
                isCompressedChats = false;
                updateChats();
                checkStorage();
                activeChat(chatListEle.firstElementChild);
                notyf.success(translations[locale]["clearChatSuccTip"]);
            }
        };
        let localSetKeys = ['modelVersion', 'APISelect', 'DeepSeekAPISelect', 'AzureAIAPISelect', 'GeminiAPISelect', 'ClaudeAPISelect', 'APIHost', 'DeepSeekAPIHost', 'AzureAIAPIHost', 'GeminiAPIHost', 'ClaudeAPIHost', 'APIKey', 'DeepSeekAPIKey', 'AzureAIAPIKey', 'GeminiAPIKey', 'ClaudeAPIKey', 'APIModel', 'DeepSeekAPIModel', 'AzureAIAPIModel', 'GeminiAPIModel', 'ClaudeAPIModel', 'hotKeys', 'userAvatar', 'system', 'temp', 'top_p', 'convWidth0', 'convWidth1', 'textSpeed', 'contLen', 'enableCOT', 'enableLongReply', 'existVoice', 'voiceTestText', 'azureRegion', 'azureKey', 'enableContVoice', 'enableAutoVoice', 'existRec', 'azureRecRegion', 'azureRecKey', 'voiceRecLang', 'autoVoiceSendWord', 'autoVoiceStopWord', 'autoVoiceSendOut', 'keepListenMic', 'fullWindow', 'themeMode', 'autoThemeMode', 'customDarkTime', 'UILang', 'pinNav', 'voice0', 'voicePitch0', 'voiceVolume0', 'voiceRate0', 'azureRole0', 'azureStyle0', 'voice1', 'voicePitch1', 'voiceVolume1', 'voiceRate1', 'azureRole1', 'azureStyle1', 'searchFlag'];
        document.getElementById("exportSet").onclick = () => {
            let data = {}
            for (let i = 0; i < localSetKeys.length; i++) {
                let key = localSetKeys[i];
                let val = localStorage.getItem(key);
                if (val != void 0) data[key] = val;
            }
            let blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
            let date = new Date();
            let fileName = "settings-" + date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + ".json";
            downBlob(blob, fileName);
            notyf.success(translations[locale]["exportSuccTip"]);
        };
        document.getElementById("importSetInput").onchange = function () {
            let file = this.files[0];
            blobToText(file).then(text => {
                try {
                    let json = JSON.parse(text);
                    let keys = Object.keys(json);
                    for (let i = 0; i < localSetKeys.length; i++) {
                        let key = localSetKeys[i];
                        let val = json[key];
                        if (val !== void 0) localStorage.setItem(key, val);
                        else localStorage.removeItem(key);
                    }
                    initSetting();
                    initVoiceVal();
                    speechServiceEle.dispatchEvent(new Event("change"));
                    initRecSetting();
                    initHotKey();
                    initLang();
                    checkStorage();
                    notyf.success(translations[locale]["importSuccTip"]);
                } catch (e) {
                    notyf.error(translations[locale]["importFailTip"]);
                }
                this.value = "";
            })
        };
        document.getElementById("resetSet").onclick = () => {
            if (confirmAction(translations[locale]["resetSetTip"])) {
                endAll();
                if (existVoice === 3) localStorage.removeItem(azureRegion + "VoiceData");
                if (existRec === 2) localStorage.removeItem(azureRecRegion + "RecData");
                let data = {};
                for (let i = 0; i < localSetKeys.length; i++) {
                    let key = localSetKeys[i];
                    let val = localStorage.removeItem(key);
                }
                initSetting();
                initVoiceVal();
                speechServiceEle.dispatchEvent(new Event("change"));
                initRecSetting();
                initHotKey();
                initLang();
                checkStorage();
                notyf.success(translations[locale]["resetSetSuccTip"]);
            }
        }
        const endAll = () => {
            endSpeak();
            if (editingIdx !== void 0) resumeSend();
            if (loading) stopLoading();
        };
        const processIdx = (plus) => {
            if (currentVoiceIdx !== void 0) currentVoiceIdx += plus;
            if (editingIdx !== void 0) editingIdx += plus;
        }
        const hotKeyVals = {};
        const ctrlHotKeyEv = (ev) => {
            if (ev.ctrlKey || ev.metaKey) {
                switch (ev.key.toLowerCase()) {
                    case hotKeyVals["Nav"]:
                        ev.preventDefault();
                        toggleNavEv();
                        return false;
                    case hotKeyVals["Search"]:
                        ev.preventDefault();
                        searchChatEle.focus();
                        return false;
                    case hotKeyVals["Input"]:
                        ev.preventDefault();
                        inputAreaEle.focus();
                        return false;
                    case hotKeyVals["NewChat"]:
                        ev.preventDefault();
                        newChatEle.dispatchEvent(new MouseEvent("click"));
                        return false;
                    case hotKeyVals["ClearChat"]:
                        ev.preventDefault();
                        clearEle.dispatchEvent(new MouseEvent("click"));
                        return false;
                    case hotKeyVals["VoiceRec"]:
                        if (supportRec) {
                            ev.preventDefault();
                            toggleRecEv();
                        }
                        return false;
                    case hotKeyVals["VoiceSpeak"]:
                        ev.preventDefault();
                        speechEvent(systemRole ? 1 : 0);
                        return false;
                }
            }
        }
        const ctrlAltHotKeyEv = (ev) => {
            if ((ev.ctrlKey || ev.metaKey) && ev.altKey) {
                switch (ev.key.toLowerCase()) {
                    case hotKeyVals["Window"]:
                        ev.preventDefault();
                        toggleFull.dispatchEvent(new Event("click"));
                        return false;
                    case hotKeyVals["Theme"]:
                        ev.preventDefault();
                        lightEle.dispatchEvent(new Event("click"));
                        return false;
                    case hotKeyVals["Lang"]:
                        ev.preventDefault();
                        let idx = localeList.indexOf(locale) + 1;
                        if (idx === localeList.length) idx = 0;
                        locale = localeList[idx];
                        setLang();
                        changeLocale();
                        return false;
                }
            }
        }
        const listKey = ['Nav', 'Search', 'Input', 'NewChat', 'ClearChat', 'VoiceRec', 'VoiceSpeak', 'Window', 'Theme', 'Lang'];
        const ctrlKeyIdx = 7;
        const defKeyVal = ['b', 'k', 'i', 'e', 'r', 'q', 's', 'u', 't', 'l'];
        const initHotKey = () => {
            let localKeysObj = {};
            let localKeys = localStorage.getItem("hotKeys");
            if (localKeys) {
                try {
                    localKeysObj = JSON.parse(localKeys);
                } catch (e) { }
            }
            let pre1 = isApple ? "⌘ + " : "Ctrl + ";
            let pre2 = isApple ? "⌘ + ⌥ + " : "Ctrl + Alt + ";
            for (let i = 0; i < listKey.length; i++) {
                let key = listKey[i];
                if (key === "VoiceRec" && !supportRec) continue;
                let ele = window["hotKey" + key];
                for (let j = 0; j < 26; j++) {
                    // top-level hotkey, can't overwrite
                    if (i < ctrlKeyIdx && (j === 13 || j === 19 || j === 22)) continue;
                    let val = String.fromCharCode(j + 97);
                    ele.options.add(new Option((i < ctrlKeyIdx ? pre1 : pre2) + val.toUpperCase(), val));
                }
                hotKeyVals[key] = ele.value = localKeysObj[key] || defKeyVal[i];
                ele.onchange = () => {
                    if (hotKeyVals[key] === ele.value) return;
                    let exist = listKey.find((item, idx) => {
                        return (i < ctrlKeyIdx ? idx < ctrlKeyIdx : idx >= ctrlKeyIdx) && hotKeyVals[item] === ele.value;
                    })
                    if (exist) {
                        ele.value = hotKeyVals[key];
                        notyf.error(translations[locale]["hotkeyConflict"])
                        return;
                    }
                    hotKeyVals[key] = ele.value;
                    localStorage.setItem("hotKeys", JSON.stringify(hotKeyVals));
                }
            }
        };
        initHotKey();
        document.addEventListener("keydown", ctrlHotKeyEv);
        document.addEventListener("keydown", ctrlAltHotKeyEv);
        const initSetting = () => {
            const apiHostEle = document.getElementById("apiHostInput");
            const deepseekHostEle = document.getElementById("deepseekApiHostInput");
            const azureAIHostEle = document.getElementById("azureAIApiHostInput");
            const geminiHostEle = document.getElementById("geminiApiHostInput");
            const claudeHostEle = document.getElementById("claudeApiHostInput");
            const apiSelectEle = document.getElementById("apiSelect");
            let localApiSelect = localStorage.getItem("APISelect");
            if (localApiSelect) {
                try {
                    apiSelects = JSON.parse(localApiSelect);
                } catch (e) {
                    apiSelects.length = 0;
                }
            } else {
                apiSelects.length = 0;
            }
            let localDeepSeekApiSelect = localStorage.getItem("DeepSeekAPISelect");
            if (localDeepSeekApiSelect) {
                try {
                    deepseekApiSelects = JSON.parse(localDeepSeekApiSelect);
                } catch (e) {
                    deepseekApiSelects.length = 0;
                }
            } else {
                deepseekApiSelects.length = 0;
            }
            let localAzureAIApiSelect = localStorage.getItem("AzureAIAPISelect");
            if (localAzureAIApiSelect) {
                try {
                    azureAIApiSelects = JSON.parse(localAzureAIApiSelect);
                } catch (e) {
                    azureAIApiSelects.length = 0;
                }
            } else {
                azureAIApiSelects.length = 0;
            }
            let localGeminiApiSelect = localStorage.getItem("GeminiAPISelect");
            if (localGeminiApiSelect) {
                try {
                    geminiApiSelects = JSON.parse(localGeminiApiSelect);
                } catch (e) {
                    geminiApiSelects.length = 0;
                }
            } else {
                geminiApiSelects.length = 0;
            }
            let localClaudeApiSelect = localStorage.getItem("ClaudeAPISelect");
            if (localClaudeApiSelect) {
                try {
                    claudeApiSelects = JSON.parse(localClaudeApiSelect);
                } catch (e) {
                    claudeApiSelects.length = 0;
                }
            } else {
                claudeApiSelects.length = 0;
            }
            let selApiSelects = apiSelects;
            let selApiKey = "APISelect";
            let selApiEle = apiHostEle;
            const delApiOption = function (ev) {
                ev.preventDefault();
                ev.stopPropagation();
                let index = Array.prototype.indexOf.call(apiSelectEle.children, this.parentElement);
                selApiSelects.splice(index, 1);
                this.parentElement.remove();
                localStorage.setItem(selApiKey, JSON.stringify(selApiSelects));
                if (!selApiSelects.includes(selApiEle.value)) {
                    selApiEle.value = selApiSelects[0] || "";
                    selApiEle.dispatchEvent(new Event("change"));
                }
                if (!selApiSelects.length) apiSelectEle.style.display = "none";
            }
            const appendApiOption = () => {
                apiSelects.push(apiHost);
                initApiOption(apiHost);
                localStorage.setItem("APISelect", JSON.stringify(apiSelects));
            }
            const appendDeepSeekApiOption = () => {
                deepseekApiSelects.push(deepseekApiHost);
                initApiOption(deepseekApiHost);
                localStorage.setItem("DeepSeekAPISelect", JSON.stringify(deepseekApiSelects));
            }
            const appendAzureAIApiOption = () => {
                azureAIApiSelects.push(azureAIApiHost);
                initApiOption(azureAIApiHost);
                localStorage.setItem("AzureAIAPISelect", JSON.stringify(azureAIApiSelects));
            }
            const appendGeminiApiOption = () => {
                geminiApiSelects.push(geminiApiHost);
                initApiOption(geminiApiHost);
                localStorage.setItem("GeminiAPISelect", JSON.stringify(geminiApiSelects));
            }
            const appendClaudeApiOption = () => {
                claudeApiSelects.push(claudeApiHost);
                initApiOption(claudeApiHost);
                localStorage.setItem("ClaudeAPISelect", JSON.stringify(claudeApiSelects));
            }
            const selApiOption = function (ev) {
                ev.preventDefault();
                ev.stopPropagation();
                apiSelectEle.style.display = "none";
                let index = Array.prototype.indexOf.call(apiSelectEle.children, this);
                selApiEle.value = selApiSelects[index];
                selApiEle.dispatchEvent(new Event("change"));
            }
            const initApiOption = (api) => {
                let optionEle = document.createElement("div");
                optionEle.onclick = selApiOption;
                let textEle = document.createElement("span");
                textEle.textContent = api;
                optionEle.appendChild(textEle);
                let delEle = document.createElement("div");
                delEle.className = "delApiOption";
                delEle.onmousedown = delApiOption;
                delEle.innerHTML = `<svg width="24" height="24"><use xlink:href="#closeIcon" /></svg>`;
                optionEle.appendChild(delEle);
                apiSelectEle.appendChild(optionEle);
            }
            const initApiSelectEle = () => {
                apiSelectEle.innerHTML = "";
                for (let i = 0; i < selApiSelects.length; i++) {
                    initApiOption(selApiSelects[i]);
                }
            }
            apiHostEle.onfocus = deepseekHostEle.onfocus = azureAIHostEle.onfocus = geminiHostEle.onfocus = claudeHostEle.onfocus = () => {
                let type = document.querySelector(".modelSwitch").querySelector(".activeSwitch").dataset.id;
                if (type === "gptOption") {
                    selApiSelects = apiSelects;
                    selApiKey = "APISelect"
                    selApiEle = apiHostEle;
                } else if (type === "deepseekOption") {
                    selApiSelects = deepseekApiSelects;
                    selApiKey = "DeepSeekAPISelect";
                    selApiEle = deepseekHostEle;
                } else if (type === "azureAIOption") {
                    selApiSelects = azureAIApiSelects;
                    selApiKey = "AzureAIAPISelect";
                    selApiEle = azureAIHostEle;
                } else if (type === "geminiOption") {
                    selApiSelects = geminiApiSelects;
                    selApiKey = "GeminiAPISelect";
                    selApiEle = geminiHostEle;
                } else {
                    selApiSelects = claudeApiSelects;
                    selApiKey = "ClaudeAPISelect";
                    selApiEle = claudeHostEle;
                }
                initApiSelectEle();
                if (selApiSelects.length) apiSelectEle.style.display = "block";
            }
            apiHostEle.onblur = deepseekHostEle.onblur = azureAIHostEle.onblur = geminiHostEle.onblur = claudeHostEle.onblur = (ev) => {
                if (!(ev.relatedTarget && apiSelectEle.contains(ev.relatedTarget))) apiSelectEle.style.display = "none";
            }
            let localApiHost = localStorage.getItem("APIHost");
            apiHost = apiHostEle.value = envAPIEndpoint || localApiHost || apiHostEle.getAttribute("value") || "";
            apiHostEle.onchange = () => {
                apiHost = apiHostEle.value;
                if (apiHost && apiSelects.indexOf(apiHost) === -1) appendApiOption();
                localStorage.setItem("APIHost", apiHost);
            }
            apiHostEle.dispatchEvent(new Event("change"));
            const keyEle = document.getElementById("keyInput");
            let localKey = localStorage.getItem("APIKey");
            customAPIKey = keyEle.value = envAPIKey || localKey || keyEle.getAttribute("value") || "";
            keyEle.onchange = () => {
                customAPIKey = keyEle.value;
                localStorage.setItem("APIKey", customAPIKey);
            }
            keyEle.dispatchEvent(new Event("change"));
            const modelEle = document.getElementById("modelInput");
            let localModel = localStorage.getItem("APIModel");
            customAPIModel = envAPIModel || localModel || modelEle.getAttribute("value") || "";
            modelEle.value = customAPIModel.slice(4);
            modelEle.onchange = () => {
                customAPIModel = modelEle.value != "" ? "gpt|" + modelEle.value : "";
                if (modelEle.value !== "") {
                    customGPTDOM.classList.remove("hide");
                    customGPTDOM.dataset.ver = customGPTDOM.lastElementChild.textContent = modelEle.value;
                    customGPTDOM.dataset.value = customAPIModel;
                } else {
                    customGPTDOM.classList.add("hide");
                }
                localStorage.setItem("APIModel", customAPIModel);
            }
            modelEle.dispatchEvent(new Event("change"));

            let localDeepSeekApiHost = localStorage.getItem("DeepSeekAPIHost");
            deepseekApiHost = deepseekHostEle.value = envDeepSeekAPIEndpoint || localDeepSeekApiHost || deepseekHostEle.getAttribute("value") || "";
            deepseekHostEle.onchange = () => {
                deepseekApiHost = deepseekHostEle.value;
                if (deepseekApiHost && deepseekApiSelects.indexOf(deepseekApiHost) === -1) appendDeepSeekApiOption();
                localStorage.setItem("DeepSeekAPIHost", deepseekApiHost);
            }
            deepseekHostEle.dispatchEvent(new Event("change"));
            const deepseekKeyEle = document.getElementById("deepseekKeyInput");
            let localDeepSeekKey = localStorage.getItem("DeepSeekAPIKey");
            deepseekAPIKey = deepseekKeyEle.value = envDeepSeekAPIKey || localDeepSeekKey || deepseekKeyEle.getAttribute("value") || "";
            deepseekKeyEle.onchange = () => {
                deepseekAPIKey = deepseekKeyEle.value;
                localStorage.setItem("DeepSeekAPIKey", deepseekAPIKey);
            }
            deepseekKeyEle.dispatchEvent(new Event("change"));
            const deepseekModelEle = document.getElementById("deepseekModelInput");
            let localDeepSeekModel = localStorage.getItem("DeepSeekAPIModel");
            deepseekAPIModel = envDeepSeekAPIModel || localDeepSeekModel || deepseekModelEle.getAttribute("value") || "";
            deepseekModelEle.value = deepseekAPIModel.slice(9);
            deepseekModelEle.onchange = () => {
                deepseekAPIModel = deepseekModelEle.value != "" ? "deepseek|" + deepseekModelEle.value : "";
                if (deepseekModelEle.value !== "") {
                    customDeepSeekDOM.classList.remove("hide");
                    customDeepSeekDOM.dataset.ver = customDeepSeekDOM.lastElementChild.textContent = deepseekModelEle.value;
                    customDeepSeekDOM.dataset.value = deepseekAPIModel;
                } else {
                    customDeepSeekDOM.classList.add("hide");
                }
                localStorage.setItem("DeepSeekAPIModel", deepseekAPIModel);
            }
            deepseekModelEle.dispatchEvent(new Event("change"));

            let localAzureAIApiHost = localStorage.getItem("AzureAIAPIHost");
            azureAIApiHost = azureAIHostEle.value = envAzureAIAPIEndpoint || localAzureAIApiHost || azureAIHostEle.getAttribute("value") || "";
            azureAIHostEle.onchange = () => {
                azureAIApiHost = azureAIHostEle.value;
                if (azureAIApiHost && azureAIApiSelects.indexOf(azureAIApiHost) === -1) appendAzureAIApiOption();
                localStorage.setItem("AzureAIAPIHost", azureAIApiHost);
            }
            azureAIHostEle.dispatchEvent(new Event("change"));
            const azureAIKeyEle = document.getElementById("azureAIKeyInput");
            let localAzureAIKey = localStorage.getItem("AzureAIAPIKey");
            azureAIAPIKey = azureAIKeyEle.value = envAzureAIAPIKey || localAzureAIKey || azureAIKeyEle.getAttribute("value") || "";
            azureAIKeyEle.onchange = () => {
                azureAIAPIKey = azureAIKeyEle.value;
                localStorage.setItem("AzureAIAPIKey", azureAIAPIKey);
            }
            azureAIKeyEle.dispatchEvent(new Event("change"));
            const azureAIModelEle = document.getElementById("azureAIModelInput");
            let localAzureAIModel = localStorage.getItem("AzureAIAPIModel");
            azureAIAPIModel = envAzureAIAPIModel || localAzureAIModel || azureAIModelEle.getAttribute("value") || "";
            azureAIModelEle.value = azureAIAPIModel.slice(6);
            azureAIModelEle.onchange = () => {
                azureAIAPIModel = azureAIModelEle.value != "" ? "azure|" + azureAIModelEle.value : "";
                if (azureAIModelEle.value !== "") {
                    customAzureAIDOM.classList.remove("hide");
                    customAzureAIDOM.lastElementChild.textContent = "Azure " + azureAIModelEle.value;
                    customAzureAIDOM.dataset.ver = azureAIModelEle.value;
                    customAzureAIDOM.dataset.value = azureAIAPIModel;
                } else {
                    customAzureAIDOM.classList.add("hide");
                }
                localStorage.setItem("AzureAIAPIModel", azureAIAPIModel);
            }
            azureAIModelEle.dispatchEvent(new Event("change"));

            let localGeminiApiHost = localStorage.getItem("GeminiAPIHost");
            geminiApiHost = geminiHostEle.value = envGeminiAPIEndpoint || localGeminiApiHost || geminiHostEle.getAttribute("value") || "";
            geminiHostEle.onchange = () => {
                geminiApiHost = geminiHostEle.value;
                if (geminiApiHost && geminiApiSelects.indexOf(geminiApiHost) === -1) appendGeminiApiOption();
                localStorage.setItem("GeminiAPIHost", geminiApiHost);
            }
            geminiHostEle.dispatchEvent(new Event("change"));
            const geminiKeyEle = document.getElementById("geminiKeyInput");
            let localGeminiKey = localStorage.getItem("GeminiAPIKey");
            geminiAPIKey = geminiKeyEle.value = envGeminiAPIKey || localGeminiKey || geminiKeyEle.getAttribute("value") || "";
            geminiKeyEle.onchange = () => {
                geminiAPIKey = geminiKeyEle.value;
                localStorage.setItem("GeminiAPIKey", geminiAPIKey);
            }
            geminiKeyEle.dispatchEvent(new Event("change"));
            const geminiModelEle = document.getElementById("geminiModelInput");
            let localGeminiModel = localStorage.getItem("GeminiAPIModel");
            geminiAPIModel = envGeminiAPIModel || localGeminiModel || geminiModelEle.getAttribute("value") || "";
            geminiModelEle.value = geminiAPIModel.slice(7);
            geminiModelEle.onchange = () => {
                geminiAPIModel = geminiModelEle.value != "" ? "gemini|" + geminiModelEle.value : "";
                if (geminiModelEle.value !== "") {
                    customGeminiDOM.classList.remove("hide");
                    customGeminiDOM.dataset.ver = customGeminiDOM.lastElementChild.textContent = geminiModelEle.value;
                    customGeminiDOM.dataset.value = geminiAPIModel;
                } else {
                    customGeminiDOM.classList.add("hide");
                }
                localStorage.setItem("GeminiAPIModel", geminiAPIModel);
            }
            geminiModelEle.dispatchEvent(new Event("change"));

            let localClaudeApiHost = localStorage.getItem("ClaudeAPIHost");
            claudeApiHost = claudeHostEle.value = envClaudeAPIEndpoint || localClaudeApiHost || claudeHostEle.getAttribute("value") || "";
            claudeHostEle.onchange = () => {
                claudeApiHost = claudeHostEle.value;
                if (claudeApiHost && claudeApiSelects.indexOf(claudeApiHost) === -1) appendClaudeApiOption();
                localStorage.setItem("ClaudeAPIHost", claudeApiHost);
            }
            claudeHostEle.dispatchEvent(new Event("change"));
            const claudeKeyEle = document.getElementById("claudeKeyInput");
            let localClaudeKey = localStorage.getItem("ClaudeAPIKey");
            claudeAPIKey = claudeKeyEle.value = envClaudeAPIKey || localClaudeKey || claudeKeyEle.getAttribute("value") || "";
            claudeKeyEle.onchange = () => {
                claudeAPIKey = claudeKeyEle.value;
                localStorage.setItem("ClaudeAPIKey", claudeAPIKey);
            }
            claudeKeyEle.dispatchEvent(new Event("change"));
            const claudeModelEle = document.getElementById("claudeModelInput");
            let localClaudeModel = localStorage.getItem("ClaudeAPIModel");
            claudeAPIModel = envClaudeAPIModel || localClaudeModel || claudeModelEle.getAttribute("value") || "";
            claudeModelEle.value = claudeAPIModel.slice(7);
            claudeModelEle.onchange = () => {
                claudeAPIModel = claudeModelEle.value != "" ? "claude|" + claudeModelEle.value : "";
                if (claudeModelEle.value !== "") {
                    customClaudeDOM.classList.remove("hide");
                    customClaudeDOM.dataset.ver = customClaudeDOM.lastElementChild.textContent = claudeModelEle.value;
                    customClaudeDOM.dataset.value = claudeAPIModel;
                } else {
                    customClaudeDOM.classList.add("hide");
                }
                localStorage.setItem("ClaudeAPIModel", claudeAPIModel);
            }
            claudeModelEle.dispatchEvent(new Event("change"));

            const modelsEle = Array.from(modelSetEle.children);
            let localModelName = localStorage.getItem("modelVersion");
            let isVailModel = modelsEle.some(item => item.dataset.value === localModelName)
            modelVersion = isVailModel ? localModelName : "gpt-4o";
            const applyModelVersion = () => {
                let activedEle = modelSetEle.querySelector(".activeModel");
                if (activedEle) activedEle.classList.remove("activeModel");
                activedEle = modelSetEle.querySelector(`[data-value="${modelVersion}"]`);
                activedEle.classList.add("activeModel");
                modelVer.textContent = activedEle.dataset.ver;
                modelType = parseInt(activedEle.dataset.type);
                modelIcon.children[0].setAttributeNS("http://www.w3.org/1999/xlink", "href", (modelType == 1 ? "#aiIcon" : modelType == 2 ? "#deepseekIcon" : modelType == 3 ? "#azureAIIcon" : modelType == 4 ? "#geminiIcon" : "#claudeIcon"));
            };
            applyModelVersion();
            modelSetEle.onclick = (ev) => {
                if (ev.target.classList.contains("modelSingle")) {
                    modelVersion = ev.target.dataset.value;
                    localStorage.setItem("modelVersion", modelVersion);
                    applyModelVersion();
                    modelSetEle.style.display = "none";
                    selectorEle.classList.remove("showModels");
                }
            }

            const updateAvatar = () => {
                setAvatarPre.src = userAvatar;
                chatlog.querySelectorAll(".request>.chatAvatar").forEach(ele => {
                    ele.children[0].src = userAvatar;
                })
            }
            let localAvatar = localStorage.getItem("userAvatar");
            userAvatar = setAvatarPre.src = setAvatar.value = localAvatar || setAvatar.getAttribute("value") || "https://cdn.jsdelivr.net/gh/xqdoo00o/chatgpt-web@main/avatar.jpg";
            setAvatar.onchange = () => {
                userAvatar = setAvatar.value;
                localStorage.setItem("userAvatar", userAvatar);
                updateAvatar();
            }
            setAvatar.dispatchEvent(new Event("change"));
            let localSystem = localStorage.getItem("system");
            systemEle.onchange = () => {
                systemRole = systemEle.value;
                localStorage.setItem("system", systemRole);
                if (systemRole) {
                    if (data[0] && data[0].role === "system") {
                        data[0].content = systemRole;
                    } else {
                        data.unshift({ role: "system", content: systemRole });
                        processIdx(1);
                    }
                } else if (data[0] && data[0].role === "system") {
                    data.shift();
                    processIdx(-1);
                }
                updateChats();
            }
            if (systemRole === void 0) {
                systemRole = systemEle.value = localSystem || presetRoleData.default || "";
                if (systemRole) {
                    data.unshift({ role: "system", content: systemRole });
                    processIdx(1);
                    updateChats();
                }
            }
            preEle.onchange = () => {
                let val = preEle.value;
                if (val && presetRoleData[val]) {
                    systemEle.value = presetRoleData[val];
                } else {
                    systemEle.value = "";
                }
                systemEle.dispatchEvent(new Event("change"));
                systemEle.focus();
            }
            const topEle = document.getElementById("top_p");
            let localTop = localStorage.getItem("top_p");
            topEle.value = roleNature = parseFloat(localTop || topEle.getAttribute("value"));
            topEle.oninput = () => {
                topEle.style.backgroundSize = (topEle.value - topEle.min) * 100 / (topEle.max - topEle.min) + "% 100%";
                roleNature = parseFloat(topEle.value);
                localStorage.setItem("top_p", topEle.value);
            }
            topEle.dispatchEvent(new Event("input"));
            const tempEle = document.getElementById("temp");
            let localTemp = localStorage.getItem("temp");
            tempEle.value = roleTemp = parseFloat(localTemp || tempEle.getAttribute("value"));
            tempEle.oninput = () => {
                tempEle.style.backgroundSize = (tempEle.value - tempEle.min) * 100 / (tempEle.max - tempEle.min) + "% 100%";
                roleTemp = parseFloat(tempEle.value);
                localStorage.setItem("temp", tempEle.value);
            }
            tempEle.dispatchEvent(new Event("input"));
            const convWEle = document.getElementById("convWidth");
            const styleSheet = document.styleSheets[0];
            convWEle.oninput = () => {
                let type = isFull ? 1 : 0;
                convWEle.style.backgroundSize = (convWEle.value - convWEle.min) * 100 / (convWEle.max - convWEle.min) + "% 100%";
                convWidth[type] = parseInt(convWEle.value);
                localStorage.setItem("convWidth" + type, convWEle.value);
                styleSheet.deleteRule(0);
                styleSheet.deleteRule(0);
                styleSheet.insertRule(`.bottom_wrapper{max-width:${convWidth[type]}%;}`, 0);
                styleSheet.insertRule(`.requestBody,.response .responseBody{max-width:calc(${convWidth[type]}% - 84px);}`, 0);
            }
            const setConvValue = () => {
                let type = isFull ? 1 : 0;
                let localConv = localStorage.getItem("convWidth" + type);
                convWEle.value = parseInt(localConv || (type ? "60" : "100"));
                convWEle.dispatchEvent(new Event("input"));
            }
            const fullFunc = () => {
                isFull = windowEle.classList.contains("full_window");
                localStorage.setItem("fullWindow", isFull);
                setConvValue();
                toggleFull.title = isFull ? translations[locale]["winedWin"] : translations[locale]["fullWin"];
                toggleFull.children[0].children[0].setAttributeNS("http://www.w3.org/1999/xlink", "href", isFull ? "#collapseFullIcon" : "#expandFullIcon");
            }
            toggleFull.onclick = () => {
                windowEle.classList.toggle("full_window");
                fullFunc();
            }
            let localFull = localStorage.getItem("fullWindow");
            if (localFull && localFull === "true") {
                if (!windowEle.classList.contains("full_window")) {
                    windowEle.classList.add("full_window");
                    fullFunc();
                }
            } else if (windowEle.classList.contains("full_window")) {
                windowEle.classList.remove("full_window");
                fullFunc();
            } else {
                setConvValue();
            }
            const speedEle = document.getElementById("textSpeed");
            let localSpeed = localStorage.getItem("textSpeed");
            speedEle.value = localSpeed || speedEle.getAttribute("value");
            textSpeed = parseFloat(speedEle.min) + (speedEle.max - speedEle.value);
            speedEle.oninput = () => {
                speedEle.style.backgroundSize = (speedEle.value - speedEle.min) * 100 / (speedEle.max - speedEle.min) + "% 100%";
                textSpeed = parseFloat(speedEle.min) + (speedEle.max - speedEle.value);
                localStorage.setItem("textSpeed", speedEle.value);
            }
            speedEle.dispatchEvent(new Event("input"));
            if (localStorage.getItem("enableCont") != null) { // fallback old cont
                if (localStorage.getItem("enableCont") === "false") localStorage.setItem("contLength", 0);
                localStorage.removeItem("enableCont");
            }
            const contLenEle = document.getElementById("contLength");
            let localContLen = localStorage.getItem("contLength");
            contLenEle.value = contLen = parseInt(localContLen || contLenEle.getAttribute("value"));
            contLenEle.oninput = () => {
                contLenEle.style.backgroundSize = (contLenEle.value - contLenEle.min) * 100 / (contLenEle.max - contLenEle.min) + "% 100%";
                contLen = parseInt(contLenEle.value);
                contLenWrap.textContent = contLen;
                localStorage.setItem("contLength", contLenEle.value);
            }
            contLenEle.dispatchEvent(new Event("input"));
            const cotEle = document.getElementById("enableCOT");
            let localCOT = localStorage.getItem("enableCOT");
            cotEle.checked = enableCOT = (localCOT || cotEle.getAttribute("checked")) === "true";
            cotEle.onchange = () => {
                enableCOT = cotEle.checked;
                localStorage.setItem("enableCOT", enableCOT);
            }
            cotEle.dispatchEvent(new Event("change"));
            const longEle = document.getElementById("enableLongReply");
            let localLong = localStorage.getItem("enableLongReply");
            longEle.checked = enableLongReply = (localLong || longEle.getAttribute("checked")) === "true";
            longEle.onchange = () => {
                enableLongReply = longEle.checked;
                localStorage.setItem("enableLongReply", enableLongReply);
            }
            longEle.dispatchEvent(new Event("change"));
            let localPin = localStorage.getItem("pinNav");
            if (window.innerWidth > 800 && !(localPin && localPin === "false")) {
                document.body.classList.add("show-nav");
            };
            const setDarkTheme = (is) => {
                let cssEle = document.body.getElementsByTagName("link")[0];
                cssEle.href = cssEle.href.replace(is ? "light" : "dark", is ? "dark" : "light");
                let hlCssEle = document.body.getElementsByTagName("link")[1];
                hlCssEle.href = hlCssEle.href.replace(is ? "github" : "github-dark", is ? "github-dark" : "github");
                justDarkTheme(is);
            }
            const handleAutoMode = (ele) => {
                if (ele.checked) {
                    autoThemeMode = parseInt(ele.value);
                    localStorage.setItem("autoThemeMode", autoThemeMode);
                    initAutoTime();
                    if (autoThemeMode) {
                        if (customDarkOut !== void 0) {
                            clearTimeout(customDarkOut);
                            customDarkOut = void 0;
                        }
                        setDarkTheme(darkMedia.matches);
                    } else {
                        checkCustomTheme();
                    }
                }
            }
            autoTheme0.onchange = autoTheme1.onchange = function () { handleAutoMode(this) };
            const handleAutoTime = (ele, idx) => {
                let otherIdx = 1 - idx;
                if (ele.value !== customDarkTime[otherIdx]) {
                    customDarkTime[idx] = ele.value;
                    localStorage.setItem("customDarkTime", JSON.stringify(customDarkTime));
                    checkCustomTheme();
                } else {
                    ele.value = customDarkTime[idx];
                    notyf.error(translations[locale]["customDarkTip"]);
                }
            }
            customStart.onchange = function () { handleAutoTime(this, 0) };
            customEnd.onchange = function () { handleAutoTime(this, 1) };
            const initAutoTime = () => {
                customAutoSet.style.display = autoThemeMode === 0 ? "block" : "none";
                if (autoThemeMode === 0) {
                    customStart.value = customDarkTime[0];
                    customEnd.value = customDarkTime[1];
                }
            }
            const initAutoThemeEle = () => {
                autoThemeEle.querySelector("#autoTheme" + autoThemeMode).checked = true;
                initAutoTime();
            }
            const checkCustomTheme = () => {
                if (customDarkOut !== void 0) clearTimeout(customDarkOut);
                let date = new Date();
                let nowTime = date.getTime();
                let start = customDarkTime[0].split(":");
                let startTime = new Date().setHours(start[0], start[1], 0, 0);
                let end = customDarkTime[1].split(":");
                let endTime = new Date().setHours(end[0], end[1], 0, 0);
                let order = endTime > startTime;
                let isDark = order ? (nowTime > startTime && endTime > nowTime) : !(nowTime > endTime && startTime > nowTime);
                let nextChange = isDark ? endTime - nowTime : startTime - nowTime;
                if (nextChange < 0) nextChange += dayMs;
                setDarkTheme(isDark);
                customDarkOut = setTimeout(() => {
                    checkCustomTheme();
                }, nextChange);
            }
            const setDarkMode = () => {
                if (customDarkOut !== void 0) {
                    clearTimeout(customDarkOut);
                    customDarkOut = void 0;
                }
                autoThemeEle.style.display = "none";
                let themeClass, title;
                if (themeMode === 2) {
                    autoThemeEle.style.display = "block";
                    if (autoThemeMode) {
                        setDarkTheme(darkMedia.matches);
                    } else {
                        checkCustomTheme();
                        initAutoThemeEle();
                    }
                    themeClass = "autoTheme";
                    title = translations[locale]["autoWord"];
                } else if (themeMode === 1) {
                    setDarkTheme(false);
                    themeClass = "lightTheme";
                    title = translations[locale]["lightTheme"];
                } else {
                    setDarkTheme(true);
                    themeClass = "darkTheme";
                    title = translations[locale]["darkTheme"];
                }
                localStorage.setItem("themeMode", themeMode);
                setLightEle.className = "setDetail themeDetail " + themeClass;
                lightEle.children[0].children[0].setAttributeNS("http://www.w3.org/1999/xlink", "href", "#" + themeClass + "Icon");
                lightEle.title = title;
            }
            lightEle.onclick = () => {
                themeMode = themeMode - 1;
                if (themeMode === -1) themeMode = 2;
                setDarkMode();
            }
            setLightEle.onclick = (ev) => {
                let idx = Array.prototype.indexOf.call(setLightEle.children, ev.target);
                if (themeMode !== idx) {
                    themeMode = idx;
                    setDarkMode();
                }
            }
            let localTheme = localStorage.getItem("themeMode");
            themeMode = parseInt(localTheme || "1");
            let localAutoTheme = localStorage.getItem("autoThemeMode");
            autoThemeMode = parseInt(localAutoTheme || "1");
            let localCustomDark = localStorage.getItem("customDarkTime");
            customDarkTime = JSON.parse(localCustomDark || '["21:00", "07:00"]');
            setDarkMode();
            darkMedia.onchange = e => {
                if (themeMode === 2 && autoThemeMode) setDarkTheme(e.matches);
            };
            const caseSearchEle = document.getElementById("matchCaseSearch");
            let localSearchFlag = localStorage.getItem("searchFlag") || "0";
            isCaseSearch = Boolean(localSearchFlag & 1);
            caseSearchEle.classList.toggle("seledSearch", isCaseSearch);
            caseSearchEle.onclick = () => {
                isCaseSearch = caseSearchEle.classList.toggle("seledSearch");
                localStorage.setItem("searchFlag", ~~isCaseSearch);
                if (searchChatEle.value.length) toSearchChats();
            }
        };
        initSetting();
        document.getElementById("loadMask").style.display = "none";
        const closeEvent = (ev) => {
            if (settingEle.contains(ev.target)) return;
            if (!dialogEle.contains(ev.target)) {
                dialogEle.style.display = "none";
                document.removeEventListener("mousedown", closeEvent, true);
                settingEle.classList.remove("showSetting");
                stopTestVoice();
            }
        }
        settingEle.onmousedown = () => {
            dialogEle.style.display = dialogEle.style.display === "block" ? "none" : "block";
            if (dialogEle.style.display === "block") {
                document.addEventListener("mousedown", closeEvent, true);
                settingEle.classList.add("showSetting");
            } else {
                document.removeEventListener("mousedown", closeEvent, true);
                settingEle.classList.remove("showSetting");
            }
        }
        const modelCloseEvent = (ev) => {
            if (selectorEle.contains(ev.target)) return;
            if (!modelSetEle.contains(ev.target)) {
                document.removeEventListener("mousedown", modelCloseEvent, true);
                modelSetEle.style.display = "none";
                selectorEle.classList.remove("showModels");
            }
        }
        selectorEle.onmousedown = () => {
            modelSetEle.style.display = modelSetEle.style.display === "block" ? "none" : "block";
            if (modelSetEle.style.display === "block") {
                document.addEventListener("mousedown", modelCloseEvent, true);
                selectorEle.classList.add("showModels");
            } else {
                document.removeEventListener("mousedown", modelCloseEvent, true);
                selectorEle.classList.remove("showModels");
            }
        }
        let delayId;
        const delay = () => {
            return new Promise((resolve) => delayId = setTimeout(resolve, textSpeed)); //打字机时间间隔
        }
        const getTime = () => {
            return existVoice === 3 ? new Date().toISOString() : new Date().toString();
        }
        const getWSPre = (date, requestId) => {
            let osPlatform = (typeof window !== "undefined") ? "Browser" : "Node";
            osPlatform += "/" + navigator.platform;
            let osName = navigator.userAgent;
            let osVersion = navigator.appVersion;
            return `Path: speech.config\r\nX-RequestId: ${requestId}\r\nX-Timestamp: ${date}\r\nContent-Type: application/json\r\n\r\n{"context":{"system":{"name":"SpeechSDK","version":"1.35.0","build":"JavaScript","lang":"JavaScript"},"os":{"platform":"${osPlatform}","name":"${osName}","version":"${osVersion}"}}}`
        }
        const getWSAudio = (date, requestId) => {
            return existVoice === 3 ? `Path: synthesis.context\r\nX-RequestId: ${requestId}\r\nX-Timestamp: ${date}\r\nContent-Type: application/json\r\n\r\n{"synthesis":{"audio":{"metadataOptions":{"bookmarkEnabled":false,"punctuationBoundaryEnabled":"false","sentenceBoundaryEnabled":"false","sessionEndEnabled":true,"visemeEnabled":false,"wordBoundaryEnabled":"false"},"outputFormat":"${voiceFormat}"},"language":{"autoDetection":false}}}`
                : `X-Timestamp:${date}\r\nContent-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":"false","wordBoundaryEnabled":"true"},"outputFormat":"${voiceFormat}"}}}}`
        }
        const getWSText = (date, requestId, lang, voice, volume, rate, pitch, style, role, msg) => {
            let fmtVolume = (volume >= 1 ? "+" : "") + (volume * 100 - 100) + "%";
            let fmtRate = (rate >= 1 ? "+" : "") + (rate * 100 - 100) + "%";
            let fmtPitch = (pitch >= 1 ? "+" : "") + (pitch - 1) + "Hz";
            if (existVoice === 3) {
                let fmtStyle = style ? ` style="${style}"` : ` style="Default"`;
                let fmtRole = role ? ` role="${role}"` : "";
                let fmtExpress = fmtStyle + fmtRole;
                return `Path: ssml\r\nX-RequestId: ${requestId}\r\nX-Timestamp: ${date}\r\nContent-Type: application/ssml+xml\r\n\r\n<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" xml:lang="${lang}"><voice name="${voice}"><lang xml:lang="${lang}"><s /><mstts:express-as${fmtExpress}><prosody pitch="${fmtPitch}" rate="${fmtRate}" volume="${fmtVolume}">${msg}</prosody></mstts:express-as><s /></lang></voice></speak>`;
            } else {
                return `X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nX-Timestamp:${date}Z\r\nPath:ssml\r\n\r\n<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="${lang}"><voice name="${voice}"><prosody pitch="${fmtPitch}" rate="${fmtRate}" volume="${fmtVolume}">${msg}</prosody></voice></speak>`;
            }
        }
        const getAzureWSURL = () => {
            return `wss://${azureRegion}.tts.speech.microsoft.com/cognitiveservices/websocket/v1?Ocp-Apim-Subscription-Key=${azureKey}`
        }
        let edgeTTSURLTmp = "";
        const WIN_EPOCH = 11644473600;
        const TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4";
        const getEdgeTTSURL = () => {
            if (edgeTTSURLTmp) return edgeTTSURLTmp;
            let currentUnix = Math.floor(Date.now() / 1e3);
            let ticks = currentUnix + WIN_EPOCH;
            let timeOut = ticks % 300;
            setTimeout(() => { edgeTTSURLTmp = "" }, (300 - timeOut) * 1e3);
            ticks = ticks - timeOut + "0000000";
            let secMsGec = createSHA256().update(ticks + TRUSTED_CLIENT_TOKEN).digest("hex").toUpperCase();
            edgeTTSURLTmp = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGec}&Sec-MS-GEC-Version=1-133.0.3065.51`;
            return edgeTTSURLTmp;
        }
        const resetSpeakIcon = () => {
            if (currentVoiceIdx !== void 0) {
                chatlog.children[systemRole ? currentVoiceIdx - 1 : currentVoiceIdx].classList.remove("showVoiceCls");
                chatlog.children[systemRole ? currentVoiceIdx - 1 : currentVoiceIdx].lastChild.lastChild.className = "voiceCls readyVoice";
            }
        }
        const endSpeak = () => {
            resetSpeakIcon();
            currentVoiceIdx = void 0;
            if (voiceIns && voiceIns instanceof Audio) {
                voiceIns.pause();
                voiceIns.currentTime = 0;
                URL.revokeObjectURL(voiceIns.src);
                voiceIns.removeAttribute("src");
                voiceIns.onended = voiceIns.onerror = null;
                sourceBuffer = void 0;
                speechPushing = false;
                if (voiceSocket && voiceSocket["pending"]) {
                    voiceSocket.close()
                }
                if (autoVoiceSocket && autoVoiceSocket["pending"]) {
                    autoVoiceSocket.close()
                }
                speechQuene.length = 0;
                autoPlayingIdx = 0;
                autoMediaSource = void 0;
                voiceContentQuene = [];
                voiceEndFlagQuene = [];
                voiceBlobURLQuene = [];
                autoOnlineVoiceFlag = false;
            } else if (supportSpe) {
                speechSynthesis.cancel();
            }
        }
        const speakEvent = (ins, force = true, end = false) => {
            return new Promise((res, rej) => {
                ins.onerror = () => {
                    if (end) {
                        endSpeak();
                    } else if (force) {
                        resetSpeakIcon();
                    }
                    res();
                }
                if (ins instanceof Audio) {
                    ins.onended = ins.onerror;
                    ins.play();
                } else {
                    ins.onend = ins.onerror;
                    speechSynthesis.speak(voiceIns);
                }
            })
        };
        let voiceData = {};
        let voiceSocket;
        let speechPushing = false;
        let speechQuene = [];
        let sourceBuffer;
        speechQuene.push = function (buffer) {
            if (!speechPushing && (sourceBuffer && !sourceBuffer.updating)) {
                speechPushing = true;
                sourceBuffer.appendBuffer(buffer);
            } else {
                Array.prototype.push.call(this, buffer)
            }
        }
        const initSocket = () => {
            return new Promise((res, rej) => {
                let url = existVoice === 3 ? getAzureWSURL() : getEdgeTTSURL();
                if (!voiceSocket || voiceSocket.readyState > 1 || voiceSocket.url !== url) {
                    if (voiceSocket && voiceSocket.readyState === 1) voiceSocket.close(1000);
                    voiceSocket = new WebSocket(url);
                    voiceSocket.binaryType = "arraybuffer";
                    voiceSocket.onopen = () => {
                        res();
                    };
                    voiceSocket.onerror = () => {
                        rej();
                    }
                } else {
                    return res();
                }
            })
        }
        const initStreamVoice = (mediaSource) => {
            return new Promise((r, j) => {
                Promise.all([initSocket(), new Promise(res => {
                    mediaSource.onsourceopen = () => {
                        res();
                    };
                })]).then(() => {
                    r();
                })
            })
        }
        let downQuene = {};
        let downSocket;
        const downBlob = (blob, name) => {
            let a = document.createElement("a");
            a.download = name;
            let url = URL.createObjectURL(blob);
            a.href = url;
            a.click();
            setTimeout(() => {
                URL.revokeObjectURL(url)
            }, 1000);
            a = null;
        }
        const initDownSocket = () => {
            return new Promise((res, rej) => {
                let url = existVoice === 3 ? getAzureWSURL() : getEdgeTTSURL();
                if (!downSocket || downSocket.readyState > 1 || downSocket.url !== url) {
                    if (downSocket && downSocket.readyState === 1) downSocket.close(1000);
                    downSocket = new WebSocket(url);
                    downSocket.binaryType = "arraybuffer";
                    downSocket.onopen = () => {
                        res();
                    };
                    downSocket.onmessage = (e) => {
                        if (e.data instanceof ArrayBuffer) {
                            let text = new TextDecoder().decode(e.data.slice(0, voicePreLen));
                            let reqIdx = text.indexOf(":");
                            let uuid = text.slice(reqIdx + 1, reqIdx + 33);
                            downQuene[uuid]["blob"].push(e.data.slice(voicePreLen));
                        } else if (e.data.indexOf("Path:turn.end") !== -1) {
                            let reqIdx = e.data.indexOf(":");
                            let uuid = e.data.slice(reqIdx + 1, reqIdx + 33);
                            let blob = new Blob(downQuene[uuid]["blob"], { type: voiceMIME });
                            let key = downQuene[uuid]["key"];
                            let name = downQuene[uuid]["name"];
                            if (blob.size === 0) {
                                notyf.open({
                                    type: "warning",
                                    message: translations[locale]["cantSpeechTip"]
                                });
                                return;
                            }
                            voiceData[key] = blob;
                            if (downQuene[uuid]["isTest"]) {
                                testVoiceBlob = blob;
                                playTestAudio();
                            } else {
                                downBlob(blob, name.slice(0, 16) + voiceSuffix);
                            }
                        }
                    }
                    downSocket.onerror = () => {
                        rej();
                    }
                } else {
                    return res();
                }
            })
        }
        const getOpenAIVoice = async (input, voice, speed) => {
            let url = apiHost + ((apiHost.length && !apiHost.endsWith("/")) ? "/" : "") + "v1/audio/speech";
            let headers = { "Content-Type": "application/json" };
            if (customAPIKey) headers["Authorization"] = "Bearer " + customAPIKey;
            let body = JSON.stringify({
                model: "tts-1",
                input,
                voice,
                response_format: "aac",
                speed
            });
            let controller = new AbortController();
            let controllerId = setTimeout(() => {
                notyf.error(translations[locale]["timeoutTip"]);
                controller.abort();
            }, 20000);
            try {
                const res = await fetch(url, {
                    method: "POST",
                    headers,
                    body,
                    signal: controller.signal
                });
                clearTimeout(controllerId);
                if (res.status === 200) {
                    return await res.blob()
                } else {
                    notyf.open({ type: "warning", message: translations[locale]["cantSpeechTip"] })
                }
            } catch (e) { }
        }
        let testVoiceBlob;
        let testVoiceIns;
        const playTestAudio = () => {
            if (existVoice >= 2) {
                if (!testVoiceIns || testVoiceIns instanceof Audio === false) {
                    testVoiceIns = new Audio();
                    testVoiceIns.onended = testVoiceIns.onerror = () => {
                        stopTestVoice();
                    }
                }
                testVoiceIns.src = URL.createObjectURL(testVoiceBlob);
                testVoiceIns.play();
            } else if (supportSpe) {
                speechSynthesis.speak(testVoiceIns);
            }
        }
        const pauseTestVoice = () => {
            if (testVoiceIns) {
                if (testVoiceIns && testVoiceIns instanceof Audio) {
                    testVoiceIns.pause();
                } else if (supportSpe) {
                    speechSynthesis.pause();
                }
            }
            testVoiceBtn.className = "justSetLine resumeTestVoice";
        }
        const resumeTestVoice = () => {
            if (testVoiceIns) {
                if (testVoiceIns && testVoiceIns instanceof Audio) {
                    testVoiceIns.play();
                } else if (supportSpe) {
                    speechSynthesis.resume();
                }
            }
            testVoiceBtn.className = "justSetLine pauseTestVoice";
        }
        const stopTestVoice = () => {
            if (testVoiceIns) {
                if (testVoiceIns instanceof Audio) {
                    testVoiceIns.pause();
                    testVoiceIns.currentTime = 0;
                    URL.revokeObjectURL(testVoiceIns.src);
                    testVoiceIns.removeAttribute("src");
                } else if (supportSpe) {
                    speechSynthesis.cancel();
                }
            }
            testVoiceBtn.className = "justSetLine readyTestVoice";
        }
        const startTestVoice = async () => {
            testVoiceBtn.className = "justSetLine pauseTestVoice";
            let volume = voiceVolume[voiceType];
            let rate = voiceRate[voiceType];
            let pitch = voicePitch[voiceType];
            let content = voiceTestText;
            if (existVoice === 4) {
                let voice = voiceRole[voiceType].name;
                let key = content + voice + rate;
                let blob = voiceData[key];
                if (blob) {
                    testVoiceBlob = blob;
                    playTestAudio();
                } else {
                    testVoiceBlob = await getOpenAIVoice(content, voice, rate);
                    if (testVoiceBlob) {
                        voiceData[key] = testVoiceBlob;
                        playTestAudio();
                    }
                }
            } else if (existVoice >= 2) {
                let voice = existVoice === 3 ? voiceRole[voiceType].ShortName : voiceRole[voiceType].Name;
                let style = azureStyle[voiceType];
                let role = azureRole[voiceType];
                let key = content + voice + volume + rate + pitch + (style ? style : "") + (role ? role : "");
                let blob = voiceData[key];
                if (blob) {
                    testVoiceBlob = blob;
                    playTestAudio();
                } else {
                    await initDownSocket();
                    let currDate = getTime();
                    let lang = voiceRole[voiceType].lang;
                    let uuid = uuidv4(existVoice === 3);
                    if (existVoice === 3) {
                        downSocket.send(getWSPre(currDate, uuid));
                    }
                    downSocket.send(getWSAudio(currDate, uuid));
                    downSocket.send(getWSText(currDate, uuid, lang, voice, volume, rate, pitch, style, role, content));
                    downSocket["pending"] = true;
                    downQuene[uuid] = {};
                    downQuene[uuid]["name"] = content;
                    downQuene[uuid]["key"] = key;
                    downQuene[uuid]["isTest"] = true;
                    downQuene[uuid]["blob"] = [];
                }
            } else {
                testVoiceIns = new SpeechSynthesisUtterance();
                testVoiceIns.onend = testVoiceIns.onerror = () => {
                    stopTestVoice();
                }
                testVoiceIns.voice = voiceRole[voiceType];
                testVoiceIns.volume = volume;
                testVoiceIns.rate = rate;
                testVoiceIns.pitch = pitch;
                testVoiceIns.text = content;
                playTestAudio();
            }
        }
        const downloadAudio = async (idx) => {
            if (existVoice < 2) {
                return;
            }
            let type = data[idx].role === "user" ? 0 : 1;
            let content = data[idx].reasoning_content ? chatlog.children[systemRole ? idx - 1 : idx].children[1].lastChild.textContent.trim() : chatlog.children[systemRole ? idx - 1 : idx].children[1].textContent.trim();
            let rate = voiceRate[type];
            if (existVoice === 4) {
                let voice = voiceRole[type].name;
                let key = content + voice + rate;
                let blob = voiceData[key];
                if (blob) {
                    downBlob(blob, content.slice(0, 16) + openAIVoiceSuffix);
                } else {
                    let resBlob = await getOpenAIVoice(content, voice, rate);
                    if (resBlob) {
                        voiceData[key] = resBlob;
                        downBlob(voiceData[key], content.slice(0, 16) + openAIVoiceSuffix);
                    }
                }
            } else {
                let voice = existVoice === 3 ? voiceRole[type].ShortName : voiceRole[type].Name;
                let volume = voiceVolume[type];
                let pitch = voicePitch[type];
                let style = azureStyle[type];
                let role = azureRole[type];
                let key = content + voice + volume + rate + pitch + (style ? style : "") + (role ? role : "");
                let blob = voiceData[key];
                if (blob) {
                    downBlob(blob, content.slice(0, 16) + voiceSuffix);
                } else {
                    await initDownSocket();
                    let currDate = getTime();
                    let lang = voiceRole[type].lang;
                    let uuid = uuidv4(existVoice === 3);
                    if (existVoice === 3) {
                        downSocket.send(getWSPre(currDate, uuid));
                    }
                    downSocket.send(getWSAudio(currDate, uuid));
                    downSocket.send(getWSText(currDate, uuid, lang, voice, volume, rate, pitch, style, role, content));
                    downSocket["pending"] = true;
                    downQuene[uuid] = {};
                    downQuene[uuid]["name"] = content;
                    downQuene[uuid]["key"] = key;
                    downQuene[uuid]["blob"] = [];
                }
            }
        }
        const NoMSEPending = (key) => {
            return new Promise((res, rej) => {
                let bufArray = [];
                voiceSocket.onmessage = (e) => {
                    if (e.data instanceof ArrayBuffer) {
                        bufArray.push(e.data.slice(voicePreLen));
                    } else if (e.data.indexOf("Path:turn.end") !== -1) {
                        voiceSocket["pending"] = false;
                        if (!(bufArray.length === 1 && bufArray[0].byteLength === 0)) {
                            voiceData[key] = new Blob(bufArray, { type: voiceMIME });
                            res(voiceData[key]);
                        } else {
                            res(new Blob());
                        }
                    }
                }
            })
        }
        const pauseEv = () => {
            if (voiceIns.src) {
                let ele = chatlog.children[systemRole ? currentVoiceIdx - 1 : currentVoiceIdx].lastChild.lastChild;
                ele.classList.remove("readyVoice");
                ele.classList.remove("pauseVoice");
                ele.classList.add("resumeVoice");
            }
        }
        const resumeEv = () => {
            if (voiceIns.src) {
                let ele = chatlog.children[systemRole ? currentVoiceIdx - 1 : currentVoiceIdx].lastChild.lastChild;
                ele.classList.remove("readyVoice");
                ele.classList.remove("resumeVoice");
                ele.classList.add("pauseVoice");
            }
        }
        const speechEvent = async (idx) => {
            if (!data[idx]) return;
            endSpeak();
            currentVoiceIdx = idx;
            if (!data[idx].content && enableContVoice) {
                if (currentVoiceIdx !== data.length - 1) { return speechEvent(currentVoiceIdx + 1) }
                else { return endSpeak() }
            };
            let type = data[idx].role === "user" ? 0 : 1;
            let dom = chatlog.children[systemRole ? idx - 1 : idx];
            dom.classList.add("showVoiceCls");
            let voiceIconEle = dom.lastChild.lastChild;
            voiceIconEle.className = "voiceCls pauseVoice";
            let content = data[idx].reasoning_content ? dom.children[1].lastChild.textContent.trim() : dom.children[1].textContent.trim();
            let volume = voiceVolume[type];
            let rate = voiceRate[type];
            let pitch = voicePitch[type];
            let style = azureStyle[type];
            let role = azureRole[type];
            if (existVoice >= 2) {
                if (!voiceIns || voiceIns instanceof Audio === false) {
                    voiceIns = new Audio();
                    voiceIns.onpause = pauseEv;
                    voiceIns.onplay = resumeEv;
                }
                if (existVoice === 4) {
                    let voice = voiceRole[type].name;
                    let key = content + voice + rate;
                    let currData = voiceData[key];
                    if (currData) {
                        voiceIns.src = URL.createObjectURL(currData);
                    } else {
                        let blob = await getOpenAIVoice(content, voice, rate);
                        if (blob) {
                            voiceData[key] = blob;
                            voiceIns.src = URL.createObjectURL(blob);
                        } else return;
                    }
                } else {
                    let voice = existVoice === 3 ? voiceRole[type].ShortName : voiceRole[type].Name;
                    let key = content + voice + volume + rate + pitch + (style ? style : "") + (role ? role : "");
                    let currData = voiceData[key];
                    if (currData) {
                        voiceIns.src = URL.createObjectURL(currData);
                    } else {
                        let mediaSource;
                        if (supportMSE) {
                            mediaSource = new MediaSource;
                            voiceIns.src = URL.createObjectURL(mediaSource);
                            await initStreamVoice(mediaSource);
                            if (!sourceBuffer) {
                                sourceBuffer = mediaSource.addSourceBuffer(voiceMIME);
                            }
                            sourceBuffer.onupdateend = function () {
                                speechPushing = false;
                                if (speechQuene.length) {
                                    let buf = speechQuene.shift();
                                    if (buf["end"]) {
                                        if (!sourceBuffer.buffered.length) notyf.open({ type: "warning", message: translations[locale]["cantSpeechTip"] });
                                        mediaSource.endOfStream();
                                    } else {
                                        speechPushing = true;
                                        sourceBuffer.appendBuffer(buf);
                                    }
                                }
                            };
                            let bufArray = [];
                            voiceSocket.onmessage = (e) => {
                                if (e.data instanceof ArrayBuffer) {
                                    let buf = e.data.slice(voicePreLen);
                                    bufArray.push(buf);
                                    speechQuene.push(buf);
                                } else if (e.data.indexOf("Path:turn.end") !== -1) {
                                    voiceSocket["pending"] = false;
                                    if (!(bufArray.length === 1 && bufArray[0].byteLength === 0)) voiceData[key] = new Blob(bufArray, { type: voiceMIME });
                                    if (!speechQuene.length && !speechPushing) {
                                        mediaSource.endOfStream();
                                    } else {
                                        let buf = new ArrayBuffer();
                                        buf["end"] = true;
                                        speechQuene.push(buf);
                                    }
                                }
                            }
                        } else {
                            await initSocket();
                        }
                        let currDate = getTime();
                        let lang = voiceRole[type].lang;
                        let uuid = uuidv4(existVoice === 3);
                        if (existVoice === 3) {
                            voiceSocket.send(getWSPre(currDate, uuid));
                        }
                        voiceSocket.send(getWSAudio(currDate, uuid));
                        voiceSocket.send(getWSText(currDate, uuid, lang, voice, volume, rate, pitch, style, role, content));
                        voiceSocket["pending"] = true;
                        if (!supportMSE) {
                            let blob = await NoMSEPending(key);
                            if (blob.size === 0) notyf.open({ type: "warning", message: translations[locale]["cantSpeechTip"] });
                            voiceIns.src = URL.createObjectURL(blob);
                        }
                    }
                }
            } else {
                voiceIns = new SpeechSynthesisUtterance();
                voiceIns.voice = voiceRole[type];
                voiceIns.volume = volume;
                voiceIns.rate = rate;
                voiceIns.pitch = pitch;
                voiceIns.text = content;
            }
            await speakEvent(voiceIns);
            if (enableContVoice) {
                if (currentVoiceIdx !== data.length - 1) { return speechEvent(currentVoiceIdx + 1) }
                else { endSpeak() }
            }
        };
        let autoVoiceSocket;
        let autoMediaSource;
        let voiceContentQuene = [];
        let voiceEndFlagQuene = [];
        let voiceBlobURLQuene = [];
        let autoOnlineVoiceFlag = false;
        const autoAddQuene = () => {
            if (voiceContentQuene.length) {
                let content = getUnescape(md.render(voiceContentQuene.shift()));
                let currDate = getTime();
                let uuid = uuidv4(existVoice === 3);
                let voice = voiceRole[1].Name;
                if (existVoice === 3) {
                    autoVoiceSocket.send(getWSPre(currDate, uuid));
                    voice = voiceRole[1].ShortName;
                }
                autoVoiceSocket.send(getWSAudio(currDate, uuid));
                autoVoiceSocket.send(getWSText(currDate, uuid, voiceRole[1].lang, voice, voiceVolume[1], voiceRate[1], voicePitch[1], azureStyle[1], azureRole[1], content));
                autoVoiceSocket["pending"] = true;
                autoOnlineVoiceFlag = true;
            }
        }
        let autoPlayingIdx = 0;
        const autoDirectAddQuene = async (index) => {
            if (voiceContentQuene.length) {
                let content = getUnescape(md.render(voiceContentQuene[voiceContentQuene.length - 1]));
                let voice = voiceRole[1].name;
                let rate = voiceRate[1];
                let blob;
                if (content !== "" && (blob = await getOpenAIVoice(content, voice, rate))) {
                    let blobURL = URL.createObjectURL(blob);
                    if (!voiceIns.src && autoPlayingIdx === index) {
                        voiceIns.src = blobURL;
                        voiceIns.play();
                    } else voiceBlobURLQuene[index] = blobURL;
                } else {
                    if (!voiceIns.src && autoPlayingIdx === index) autoPlayNext();
                    else voiceBlobURLQuene[index] = null;
                }
                if (voiceEndFlagQuene.shift()) {
                    if (!voiceIns.src) endSpeak();
                    else voiceBlobURLQuene.push("end");
                }
            }
        }
        const autoPlayNext = () => {
            autoPlayingIdx += 1;
            if (voiceBlobURLQuene.length) {
                let src = voiceBlobURLQuene[autoPlayingIdx];
                if (src === "end") {
                    endSpeak();
                } else if (src === null) {
                    autoPlayNext();
                } else if (src) {
                    voiceIns.src = src;
                    voiceIns.currentTime = 0;
                    voiceIns.play();
                } else {
                    voiceIns.currentTime = 0;
                    voiceIns.removeAttribute("src");
                }
            } else {
                voiceIns.currentTime = 0;
                voiceIns.removeAttribute("src");
            }
        }
        const autoSpeechEvent = (content, ele, force = false, end = false) => {
            if (ele.lastChild.lastChild.classList.contains("readyVoice")) {
                ele.classList.add("showVoiceCls");
                ele.lastChild.lastChild.className = "voiceCls pauseVoice";
            }
            if (existVoice >= 2) {
                voiceContentQuene.push(content);
                voiceEndFlagQuene.push(end);
                if (!voiceIns || voiceIns instanceof Audio === false) {
                    voiceIns = new Audio();
                    voiceIns.onpause = pauseEv;
                    voiceIns.onplay = resumeEv;
                }
                if (existVoice === 4) {
                    autoDirectAddQuene(voiceContentQuene.length - 1);
                    voiceIns.onended = voiceIns.onerror = () => { autoPlayNext() };
                } else {
                    let url = existVoice === 3 ? getAzureWSURL() : getEdgeTTSURL();
                    if (!autoVoiceSocket || autoVoiceSocket.readyState > 1 || autoVoiceSocket.url !== url) {
                        if (autoVoiceSocket && autoVoiceSocket.readyState === 1) autoVoiceSocket.close(1000);
                        autoVoiceSocket = new WebSocket(url);
                        autoVoiceSocket.binaryType = "arraybuffer";
                        autoVoiceSocket.onopen = () => {
                            autoAddQuene();
                        };
                        autoVoiceSocket.onerror = () => {
                            autoOnlineVoiceFlag = false;
                        };
                    };
                    let bufArray = [];
                    autoVoiceSocket.onmessage = (e) => {
                        if (e.data instanceof ArrayBuffer) {
                            (supportMSE ? speechQuene : bufArray).push(e.data.slice(voicePreLen));
                        } else {
                            if (e.data.indexOf("Path:turn.end") !== -1) {
                                autoVoiceSocket["pending"] = false;
                                autoOnlineVoiceFlag = false;
                                if (!supportMSE) {
                                    let blob = new Blob(bufArray, { type: voiceMIME });
                                    bufArray = [];
                                    if (blob.size) {
                                        let blobURL = URL.createObjectURL(blob);
                                        if (!voiceIns.src) {
                                            voiceIns.src = blobURL;
                                            voiceIns.play();
                                        } else {
                                            voiceBlobURLQuene.push(blobURL);
                                        }
                                    } else {
                                        notyf.open({ type: "warning", message: translations[locale]["cantSpeechTip"] });
                                    }
                                    autoAddQuene();
                                }
                                if (voiceEndFlagQuene.shift()) {
                                    if (supportMSE) {
                                        if (!speechQuene.length && !speechPushing) {
                                            autoMediaSource.endOfStream();
                                        } else {
                                            let buf = new ArrayBuffer();
                                            buf["end"] = true;
                                            speechQuene.push(buf);
                                        }
                                    } else {
                                        if (!voiceBlobURLQuene.length && !voiceIns.src) {
                                            endSpeak();
                                        } else {
                                            voiceBlobURLQuene.push("end");
                                        }
                                    }
                                };
                                if (supportMSE) {
                                    autoAddQuene();
                                }
                            }
                        }
                    };
                    if (!autoOnlineVoiceFlag && autoVoiceSocket.readyState) {
                        autoAddQuene();
                    }
                    if (supportMSE) {
                        if (!autoMediaSource) {
                            autoMediaSource = new MediaSource();
                            autoMediaSource.onsourceopen = () => {
                                if (!sourceBuffer) {
                                    sourceBuffer = autoMediaSource.addSourceBuffer(voiceMIME);
                                    sourceBuffer.onupdateend = () => {
                                        speechPushing = false;
                                        if (speechQuene.length) {
                                            let buf = speechQuene.shift();
                                            if (buf["end"]) {
                                                if (!sourceBuffer.buffered.length) notyf.open({ type: "warning", message: translations[locale]["cantSpeechTip"] });
                                                autoMediaSource.endOfStream();
                                            } else {
                                                speechPushing = true;
                                                sourceBuffer.appendBuffer(buf);
                                            }
                                        }
                                    };
                                }
                            }
                        }
                        if (!voiceIns.src) {
                            voiceIns.src = URL.createObjectURL(autoMediaSource);
                            voiceIns.play();
                            voiceIns.onended = voiceIns.onerror = () => {
                                endSpeak();
                            }
                        }
                    } else {
                        voiceIns.onended = voiceIns.onerror = () => {
                            if (voiceBlobURLQuene.length) {
                                let src = voiceBlobURLQuene.shift();
                                if (src === "end") {
                                    endSpeak();
                                } else {
                                    voiceIns.src = src;
                                    voiceIns.currentTime = 0;
                                    voiceIns.play();
                                }
                            } else {
                                voiceIns.currentTime = 0;
                                voiceIns.removeAttribute("src");
                            }
                        }
                    }
                }
            } else {
                voiceIns = new SpeechSynthesisUtterance(content);
                voiceIns.volume = voiceVolume[1];
                voiceIns.rate = voiceRate[1];
                voiceIns.pitch = voicePitch[1];
                voiceIns.voice = voiceRole[1];
                speakEvent(voiceIns, force, end);
            }
        };
        const confirmAction = (prompt) => {
            if (window.confirm(prompt)) { return true }
            else { return false }
        };
        const findLastSpecialCharIndex = (text) => {
            const specialChars = new Set(['.', '?', '!', '~', '。', '？', '！', '\n']);
            for (let i = text.length - 1; i >= 0; i--) {
                if (specialChars.has(text[i])) {
                    return i;
                }
            }
            return -1;
        };
        const toggleCot = (dom) => {
            dom.classList.toggle("down");
            dom.nextElementSibling.classList.toggle("hide");
        };
        const endCot = (dom) => {
            dom.classList.add("endCot");
            dom.setAttribute("data-i18n-key", "thinked");
            dom.textContent = translations[locale]["thinked"];
        };
        let currentModelName;
        let autoVoiceIdx = 0;
        let autoVoiceDataIdx;
        let refreshIdx;
        let currentResEle;
        let cotData = "";
        let loadingCOT = 0;
        let unexpectedCutOff = "";
        let progressData = "";
        const streamGen = async (long) => {
            currentModelName = modelVersion;
            controller = new AbortController();
            controllerId = setTimeout(() => {
                notyf.error(translations[locale]["timeoutTip"]);
                stopLoading();
            }, 200000);
            let isRefresh = refreshIdx !== void 0;
            if (isRefresh) {
                currentResEle = chatlog.children[systemRole ? refreshIdx - 1 : refreshIdx];
                if (outOfMsgWindow(currentResEle)) messagesEle.scrollTo(0, currentResEle.offsetTop)
            } else if (!currentResEle) {
                currentResEle = createConvEle("response", true, modelVersion);
                currentResEle.children[1].innerHTML = "<p class='cursorCls'><br /></p>";
                currentResEle.dataset.loading = true;
                scrollToBottom();
            }
            let idx = isRefresh ? refreshIdx : data.length;
            if (existVoice && enableAutoVoice && !long) {
                if (isRefresh) {
                    endSpeak();
                    autoVoiceDataIdx = currentVoiceIdx = idx;
                } else if (currentVoiceIdx !== data.length) {
                    endSpeak();
                    autoVoiceDataIdx = currentVoiceIdx = idx;
                }
            };
            try {
                let dataSlice;
                if (long) {
                    idx = isRefresh ? refreshIdx : data.length - 1;
                    dataSlice = [data[idx - 1], data[idx]];
                    if (systemRole) dataSlice.unshift(data[0]);
                } else {
                    let startIdx = idx > contLen ? idx - contLen - 1 : 0;
                    dataSlice = data.slice(startIdx, idx);
                    if (systemRole && startIdx > 0) dataSlice.unshift(data[0]);
                }
                let headers = { "Content-Type": "application/json" };
                let url, body, model = currentModelName.split("|").pop();
                if (modelType === 1) {
                    dataSlice = dataSlice.map(item => {
                        if (item.role === "assistant") return { role: item.role, content: item.content };
                        else return item;
                    })
                    url = apiHost + ((apiHost.length && !apiHost.endsWith("/")) ? "/" : "") + API_URL;
                    if (customAPIKey) headers["Authorization"] = "Bearer " + customAPIKey;
                    body = JSON.stringify({
                        messages: dataSlice,
                        model,
                        stream: true,
                        temperature: roleTemp,
                        top_p: roleNature
                    });
                } else if (modelType === 2) {
                    dataSlice = dataSlice.map(item => {
                        if (item.role === "assistant") return { role: item.role, content: item.content };
                        else return item;
                    })
                    url = deepseekApiHost + ((deepseekApiHost.length && !deepseekApiHost.endsWith("/")) ? "/" : "") + DeepSeek_API_URL;
                    if (deepseekAPIKey) headers["Authorization"] = "Bearer " + deepseekAPIKey;
                    body = JSON.stringify({
                        messages: dataSlice,
                        model,
                        stream: true,
                        temperature: roleTemp,
                        top_p: roleNature
                    });
                } else if (modelType === 3) {
                    dataSlice = dataSlice.map(item => {
                        if (item.role === "assistant") return { role: item.role, content: item.content };
                        else return item;
                    })
                    url = azureAIApiHost;
                    if (azureAIAPIKey) headers["Api-Key"] = azureAIAPIKey;
                    body = JSON.stringify({
                        messages: dataSlice,
                        model,
                        stream: true,
                        temperature: roleTemp,
                        top_p: roleNature
                    });
                } else if (modelType === 4) {
                    dataSlice = dataSlice.map(item => {
                        return { role: item.role === "assistant" ? "model" : "user", parts: [{ text: item.content }] };
                    })
                    url = geminiApiHost + ((geminiApiHost.length && !geminiApiHost.endsWith("/")) ? "/" : "") + Gemini_API_URL + model + `:streamGenerateContent?alt=sse&key=${geminiAPIKey}`;
                    body = JSON.stringify({
                        contents: dataSlice,
                        generationConfig: {
                            temperature: roleTemp,
                            topP: roleNature
                        }
                    });
                } else {
                    let system;
                    if (systemRole) {
                        system = dataSlice.shift().content;
                    }
                    dataSlice = dataSlice.map(item => {
                        if (item.role === "assistant") return { role: item.role, content: item.content };
                        else return item;
                    })
                    url = claudeApiHost + ((claudeApiHost.length && !claudeApiHost.endsWith("/")) ? "/" : "") + Claude_API_URL;
                    if (claudeAPIKey) headers["x-api-key"] = claudeAPIKey;
                    headers["anthropic-version"] = "2023-06-01";
                    body = JSON.stringify({
                        model,
                        messages: dataSlice,
                        max_tokens: 4096,
                        stream: true,
                        temperature: roleTemp,
                        top_p: roleNature,
                        ...(system ? { system } : {})
                    });
                }
                const res = await fetch(url, {
                    method: "POST",
                    headers,
                    body,
                    signal: controller.signal
                });
                clearTimeout(controllerId);
                controllerId = void 0;
                if (res.status !== 200) {
                    if (res.status === 401) {
                        notyf.error(translations[locale]["errorAiKeyTip"])
                    } else if (res.status === 400 || res.status === 413) {
                        notyf.error(translations[locale]["largeReqTip"]);
                    } else if (res.status === 404) {
                        notyf.error(translations[locale]["noModelPerTip"]);
                    } else if (res.status === 429) {
                        notyf.error(res.statusText ? translations[locale]["apiRateTip"] : translations[locale]["exceedLimitTip"]);
                    } else {
                        notyf.error(translations[locale]["badGateTip"]);
                    }
                    stopLoading();
                    return;
                }
                let container = document.createElement("div");
                let cotBody;
                let mdBody = currentResEle.children[1];
                const decoder = new TextDecoder();
                const reader = res.body.getReader();
                let readChunk;
                if (modelType <= 3) {
                    readChunk = async () => {
                        return reader.read().then(async ({ value, done }) => {
                            if (!done) {
                                value = decoder.decode(value);
                                if (unexpectedCutOff) {
                                    value += unexpectedCutOff;
                                    unexpectedCutOff = "";
                                };
                                let chunks = value.match(/[^\n]+/g);
                                if (!chunks) return readChunk();
                                let payload;
                                for (let i = 0; i < chunks.length; i++) {
                                    let chunk = chunks[i];
                                    if (chunk) {
                                        try {
                                            payload = JSON.parse(chunk.slice(5));
                                        } catch (e) {
                                            if (chunk.startsWith("data:")) unexpectedCutOff = chunk;
                                            continue;
                                        }
                                        if (!payload.choices.length) continue;
                                        if (payload.choices[0].finish_reason) {
                                            let lenStop = payload.choices[0].finish_reason === "length";
                                            longReplyFlag = enableLongReply && lenStop;
                                            let ele = currentResEle.lastChild.children[0].children[0];
                                            if (!enableLongReply && lenStop) { ele.className = "halfRefReq optionItem"; ele.title = translations[locale]["continue"] }
                                            else { ele.className = "refreshReq optionItem"; ele.title = translations[locale]["refresh"] };
                                            if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx) {
                                                let voiceText = longReplyFlag ? "" : progressData.slice(autoVoiceIdx), stop = !longReplyFlag;
                                                autoSpeechEvent(voiceText, currentResEle, false, stop);
                                            }
                                            break;
                                        } else {
                                            if (enableCOT && payload.choices[0].delta.reasoning_content != null) {
                                                loadingCOT = 1;
                                                if (cotBody == void 0) {
                                                    cotBody = document.createElement("div");
                                                    cotBody.innerHTML = `<div class="cotBtn" data-i18n-key="thinking" onclick="toggleCot(this, event)"></div><div class="cotContent"></div>`
                                                    mdBody.innerHTML = "";
                                                    mdBody.appendChild(cotBody);
                                                    cotBody.firstChild.textContent = translations[locale]["thinking"];
                                                    let newMdBody = document.createElement("div");
                                                    newMdBody.classList.add("markdown-body");
                                                    mdBody.appendChild(newMdBody);
                                                    mdBody = newMdBody;
                                                }
                                                cotData += payload.choices[0].delta.reasoning_content;
                                                container.innerHTML = md.render(cotData);
                                                if (container.children.length > 1 && cotBody.lastChild.children.length === container.children.length) {
                                                    morphdom(cotBody.lastChild.lastElementChild, container.lastElementChild);
                                                } else {
                                                    morphdom(cotBody.lastChild, container, { childrenOnly: true });
                                                }
                                                scrollToBottom();
                                            }
                                            let content = payload.choices[0].delta.content;
                                            if (content != null && content !== "") {
                                                if (loadingCOT == 1 && cotData) {
                                                    loadingCOT = 0;
                                                    endCot(cotBody.firstChild);
                                                }
                                                if (cotData == "" && content === "<think>") {
                                                    loadingCOT = 2;
                                                    if (!enableCOT) continue;
                                                    if (cotBody == void 0) {
                                                        cotBody = document.createElement("div");
                                                        cotBody.innerHTML = `<div class="cotBtn" data-i18n-key="thinking" onclick="toggleCot(this, event)"></div><div class="cotContent"></div>`
                                                        mdBody.innerHTML = "";
                                                        mdBody.appendChild(cotBody);
                                                        cotBody.firstChild.textContent = translations[locale]["thinking"];
                                                        let newMdBody = document.createElement("div");
                                                        newMdBody.classList.add("markdown-body");
                                                        mdBody.appendChild(newMdBody);
                                                        mdBody = newMdBody;
                                                    }
                                                    continue;
                                                }
                                                if (content === "</think>") {
                                                    loadingCOT = 0;
                                                    if (!enableCOT) continue;
                                                    endCot(cotBody.firstChild);
                                                    continue;
                                                }
                                                if (loadingCOT === 2) {
                                                    if (!enableCOT) continue;
                                                    cotData += content;
                                                    container.innerHTML = md.render(cotData);
                                                    if (container.children.length > 1 && cotBody.lastChild.children.length === container.children.length) {
                                                        morphdom(cotBody.lastChild.lastElementChild, container.lastElementChild);
                                                    } else {
                                                        morphdom(cotBody.lastChild, container, { childrenOnly: true });
                                                    }
                                                    scrollToBottom();
                                                    continue;
                                                }
                                                if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx) {
                                                    let spliter = findLastSpecialCharIndex(content);
                                                    if (spliter != -1) {
                                                        let voiceText = progressData.slice(autoVoiceIdx) + content.slice(0, spliter + 1);
                                                        autoVoiceIdx += voiceText.length;
                                                        autoSpeechEvent(voiceText, currentResEle);
                                                    }
                                                }
                                                if (progressData && textSpeed) await delay();
                                                progressData += content;
                                                container.innerHTML = md.render(progressData);
                                                if (container.children.length > 1 && mdBody.children.length === container.children.length) {
                                                    morphdom(mdBody.lastElementChild, container.lastElementChild);
                                                } else {
                                                    morphdom(mdBody, container, { childrenOnly: true });
                                                }
                                                scrollToBottom();
                                            }
                                        }
                                    }
                                }
                                return readChunk();
                            } else {
                                if (isRefresh) {
                                    data[refreshIdx].content = progressData;
                                    data[refreshIdx].model = currentModelName;
                                    if (cotData) data[refreshIdx].reasoning_content = cotData;
                                    else delete data[refreshIdx].reasoning_content;
                                } else {
                                    if (long) {
                                        data[data.length - 1].content = progressData;
                                        if (cotData) data[data.length - 1].reasoning_content = cotData;
                                        else delete data[data.length - 1].reasoning_content;
                                    } else {
                                        let dat = { role: "assistant", content: progressData, model: currentModelName };
                                        if (cotData) dat.reasoning_content = cotData;
                                        data.push(dat);
                                    }
                                }
                                if (longReplyFlag) return streamGen(true);
                                stopLoading(false);
                            }
                        });
                    };
                } else if (modelType === 4) {
                    readChunk = async () => {
                        return reader.read().then(async ({ value, done }) => {
                            if (!done) {
                                value = decoder.decode(value);
                                let chunks = value.match(/[^\r\n]+/g);
                                if (!chunks) return readChunk();
                                let payload;
                                for (let i = 0; i < chunks.length; i++) {
                                    let chunk = chunks[i];
                                    if (chunk) {
                                        try {
                                            payload = JSON.parse(chunk.slice(5));
                                        } catch (e) {
                                            break;
                                        }
                                        if (!payload.candidates.length) continue;
                                        let content = payload.candidates[0].content.parts[0].text;
                                        if (content != null && content !== "") {
                                            if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx) {
                                                let spliter = findLastSpecialCharIndex(content);
                                                if (spliter != -1) {
                                                    let voiceText = progressData.slice(autoVoiceIdx) + content.slice(0, spliter + 1);
                                                    autoVoiceIdx += voiceText.length;
                                                    autoSpeechEvent(voiceText, currentResEle);
                                                }
                                            }
                                            if (progressData && textSpeed) await delay();
                                            progressData += content;
                                            container.innerHTML = md.render(progressData);
                                            if (container.children.length > 1 && currentResEle.children[1].children.length === container.children.length) {
                                                morphdom(currentResEle.children[1].lastElementChild, container.lastElementChild);
                                            } else {
                                                morphdom(currentResEle.children[1], container, { childrenOnly: true });
                                            }
                                            scrollToBottom();
                                        }
                                        if (payload.candidates[0].finishReason) {
                                            let lenStop = payload.candidates[0].finishReason === "MAX_TOKENS";
                                            longReplyFlag = enableLongReply && lenStop;
                                            let ele = currentResEle.lastChild.children[0].children[0];
                                            if (!enableLongReply && lenStop) { ele.className = "halfRefReq optionItem"; ele.title = translations[locale]["continue"] }
                                            else { ele.className = "refreshReq optionItem"; ele.title = translations[locale]["refresh"] };
                                            if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx) {
                                                let voiceText = longReplyFlag ? "" : progressData.slice(autoVoiceIdx), stop = !longReplyFlag;
                                                autoSpeechEvent(voiceText, currentResEle, false, stop);
                                            }
                                            break;
                                        }
                                    }
                                }
                                return readChunk();
                            } else {
                                if (isRefresh) {
                                    data[refreshIdx].content = progressData;
                                    data[refreshIdx].model = currentModelName;
                                    if (cotData) data[refreshIdx].reasoning_content = cotData;
                                    else delete data[refreshIdx].reasoning_content;
                                } else {
                                    if (long) {
                                        data[data.length - 1].content = progressData
                                        if (cotData) data[data.length - 1].reasoning_content = cotData;
                                        else delete data[data.length - 1].reasoning_content;
                                    } else { data.push({ role: "assistant", content: progressData, model: currentModelName }) }
                                }
                                if (longReplyFlag) return streamGen(true);
                                stopLoading(false);
                            }
                        });
                    };
                } else {
                    readChunk = async () => {
                        return reader.read().then(async ({ value, done }) => {
                            if (!done) {
                                value = decoder.decode(value);
                                let chunks = value.match(/[^\n]+/g);
                                if (!chunks) return readChunk();
                                let event;
                                let payload;
                                for (let i = 0; i < chunks.length; i++) {
                                    let chunk = chunks[i];
                                    if (chunk) {
                                        try {
                                            if (chunk.startsWith("event")) {
                                                event = chunk.slice(7);
                                            } else {
                                                payload = JSON.parse(chunk.slice(5));
                                            }
                                        } catch (e) {
                                            break;
                                        }
                                        if (chunk.startsWith("event")) continue;
                                        if (event === "message_delta") {
                                            let lenStop = payload.delta.stop_reason === "max_tokens";
                                            longReplyFlag = enableLongReply && lenStop;
                                            let ele = currentResEle.lastChild.children[0].children[0];
                                            if (!enableLongReply && lenStop) { ele.className = "halfRefReq optionItem"; ele.title = translations[locale]["continue"] }
                                            else { ele.className = "refreshReq optionItem"; ele.title = translations[locale]["refresh"] };
                                            if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx) {
                                                let voiceText = longReplyFlag ? "" : progressData.slice(autoVoiceIdx), stop = !longReplyFlag;
                                                autoSpeechEvent(voiceText, currentResEle, false, stop);
                                            }
                                            break;
                                        } else if (event === "content_block_delta") {
                                            if (payload.delta.type !== "text_delta") continue;
                                            let content = payload.delta.text;
                                            if (content != null && content !== "") {
                                                if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx) {
                                                    let spliter = findLastSpecialCharIndex(content);
                                                    if (spliter != -1) {
                                                        let voiceText = progressData.slice(autoVoiceIdx) + content.slice(0, spliter + 1);
                                                        autoVoiceIdx += voiceText.length;
                                                        autoSpeechEvent(voiceText, currentResEle);
                                                    }
                                                }
                                                if (progressData && textSpeed) await delay();
                                                progressData += content;
                                                container.innerHTML = md.render(progressData);
                                                if (container.children.length > 1 && currentResEle.children[1].children.length === container.children.length) {
                                                    morphdom(currentResEle.children[1].lastElementChild, container.lastElementChild);
                                                } else {
                                                    morphdom(currentResEle.children[1], container, { childrenOnly: true });
                                                }
                                                scrollToBottom();
                                            }
                                        }
                                    }
                                }
                                return readChunk();
                            } else {
                                if (isRefresh) {
                                    data[refreshIdx].content = progressData;
                                    data[refreshIdx].model = currentModelName;
                                    if (cotData) data[refreshIdx].reasoning_content = cotData;
                                    else delete data[refreshIdx].reasoning_content;
                                } else {
                                    if (long) {
                                        data[data.length - 1].content = progressData
                                        if (cotData) data[data.length - 1].reasoning_content = cotData;
                                        else delete data[data.length - 1].reasoning_content;
                                    }
                                    else { data.push({ role: "assistant", content: progressData, model: currentModelName }) }
                                }
                                if (longReplyFlag) return streamGen(true);
                                stopLoading(false);
                            }
                        });
                    };
                }
                await readChunk();
                container = null;
            } catch (e) {
                if (e.message.indexOf("aborted") === -1) {
                    notyf.error(translations[locale]["badEndpointTip"])
                    stopLoading();
                }
            }
        };
        const loadAction = (bool) => {
            loading = bool;
            sendBtnEle.disabled = bool;
            sendBtnEle.className = bool ? " loading" : "loaded";
            stopEle.style.display = bool ? "flex" : "none";
            textInputEvent();
        };
        const updateChatPre = () => {
            let ele = activeChatEle.children[1].children[1];
            let first = data.find(item => { return item.role === "assistant" });
            ele.textContent = first ? first.content.slice(0, 30) : "";
            forceRepaint(ele.parentElement)
        }
        const stopLoading = (abort = true) => {
            stopEle.style.display = "none";
            if (currentResEle.children[1].querySelector(".cursorCls")) currentResEle.children[1].innerHTML = "<br />";
            if (abort) {
                controller.abort();
                if (controllerId) clearTimeout(controllerId);
                if (delayId) clearTimeout(delayId);
                if (refreshIdx !== void 0) {
                    data[refreshIdx].content = progressData;
                    data[refreshIdx].model = currentModelName;
                    if (cotData) data[refreshIdx].reasoning_content = cotData;
                    else delete data[refreshIdx].reasoning_content;
                }
                else if (data[data.length - 1].role === "assistant") {
                    data[data.length - 1].content = progressData;
                    data[data.length - 1].model = currentModelName;
                    if (cotData) data[data.length - 1].reasoning_content = cotData;
                    else delete data[data.length - 1].reasoning_content;
                }
                else {
                    let dat = { role: "assistant", content: progressData, model: currentModelName };
                    if (cotData) dat.reasoning_content = cotData;
                    data.push(dat);
                }
                if (existVoice && enableAutoVoice && currentVoiceIdx === autoVoiceDataIdx && progressData.length) {
                    let voiceText = progressData.slice(autoVoiceIdx);
                    autoSpeechEvent(voiceText, currentResEle, false, true);
                }
            }
            if (activeChatEle.children[1].children[1].textContent === "") updateChatPre();
            updateChats();
            controllerId = delayId = refreshIdx = autoVoiceDataIdx = void 0;
            autoVoiceIdx = 0;
            currentResEle.dataset.loading = false;
            currentResEle = null;
            progressData = "";
            cotData = "";
            loadingCOT = 0;
            unexpectedCutOff = "";
            loadAction(false);
        };
        const generateText = (message) => {
            loadAction(true);
            let requestEle;
            let isBottom = isContentBottom();
            if (editingIdx !== void 0) {
                let idx = editingIdx;
                let eleIdx = systemRole ? idx - 1 : idx;
                requestEle = chatlog.children[eleIdx];
                data[idx].content = message;
                resumeSend();
                if (idx !== data.length - 1) {
                    requestEle.children[1].textContent = message;
                    if (data[idx + 1].role !== "assistant") {
                        if (currentVoiceIdx !== void 0) {
                            if (currentVoiceIdx > idx) { currentVoiceIdx++ }
                        }
                        data.splice(idx + 1, 0, { role: "assistant", content: "", model: modelVersion });
                        chatlog.insertBefore(createConvEle("response", false, modelVersion), chatlog.children[eleIdx + 1]);
                    } else formatAvatarEle(chatlog.children[eleIdx + 1].children[0], modelVersion);
                    chatlog.children[eleIdx + 1].children[1].innerHTML = "<p class='cursorCls'><br /></p>";
                    chatlog.children[eleIdx + 1].dataset.loading = true;
                    idx = idx + 1;
                    data[idx].content = "";
                    if (idx === currentVoiceIdx) { endSpeak() };
                    refreshIdx = idx;
                    updateChats();
                    streamGen();
                    return;
                }
            } else {
                requestEle = createConvEle("request");
                data.push({ role: "user", content: message });
            }
            requestEle.children[1].textContent = message;
            if (chatsData[activeChatIdx].name === translations[locale]["newChatName"]) {
                if (message.length > 20) message = message.slice(0, 17) + "...";
                chatsData[activeChatIdx].name = message;
                activeChatEle.children[1].children[0].textContent = message;
            }
            updateChats();
            if (isBottom) messagesEle.scrollTo(0, messagesEle.scrollHeight);
            streamGen();
        };
        inputAreaEle.onkeydown = (e) => {
            if (e.keyCode === 13 && !e.shiftKey) {
                e.preventDefault();
                genFunc();
            } else if (keepListenMic && recing) {
                resetRecRes();
            }
        };
        const genFunc = async function () {
            clearAutoSendTimer();
            if (recing) {
                if (existRec === 3) await toggleRecEv(false);
                else if (existRec === 2) toggleRecEv(false);
                else if (!keepListenMic) toggleRecEv();
            }
            let message = inputAreaEle.value.trim();
            if (message.length !== 0 && noLoading()) {
                inputAreaEle.value = "";
                inputAreaEle.style.height = "47px";
                if (keepListenMic && recing) resetRecRes();
                generateText(message);
            }
        };
        sendBtnEle.onclick = genFunc;
        stopEle.onclick = stopLoading;
        clearEle.onclick = () => {
            if (editingIdx === void 0) {
                if (noLoading() && confirmAction(translations[locale]["clearChatTip"])) {
                    endSpeak();
                    if (systemRole) { data.length = 1 }
                    else { data.length = 0 }
                    chatlog.innerHTML = "";
                    updateChatPre();
                    updateChats();
                }
            } else {
                resumeSend();
            }
        }
    </script>
    <link crossorigin="anonymous" href="https://fastly.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css"
        rel="stylesheet">
    <script defer>
        let downRoleController = new AbortController();
        const loadPrompt = () => {
            downRoleController.abort();
            downRoleController = new AbortController();
            setTimeout(() => {
                downRoleController.abort();
            }, 10000);
            preEle.options.length = 5;
            if (locale === "zh") {
                fetch("https://fastly.jsdelivr.net/gh/PlexPt/awesome-chatgpt-prompts-zh/prompts-zh.json", {
                    signal: downRoleController.signal
                }).then(async (response) => {
                    let res = await response.json();
                    for (let i = 0; i < res.length; i++) {
                        let key = "act" + i;
                        presetRoleData[key] = res[i].prompt.trim();
                        let optionEle = document.createElement("option");
                        optionEle.text = res[i].act;
                        optionEle.value = key;
                        preEle.options.add(optionEle);
                    }
                }).catch(e => { })
            } else {
                fetch("https://fastly.jsdelivr.net/gh/f/awesome-chatgpt-prompts/prompts.csv", {
                    signal: downRoleController.signal
                }).then(async (response) => {
                    let res = await response.text();
                    let arr = res.split("\n");
                    for (let i = 1; i < arr.length - 1; i++) {
                        let key = "act" + i;
                        let index = arr[i].indexOf(",");
                        presetRoleData[key] = arr[i].slice(index + 2, -1);
                        let optionEle = document.createElement("option");
                        optionEle.text = arr[i].slice(1, index - 1);
                        optionEle.value = key;
                        preEle.options.add(optionEle);
                    }
                }).catch(e => { })
            }
        }
        loadPrompt();
    </script>
</body>

</html>